React Interface v3
This commit is contained in:
@@ -1,33 +1,60 @@
|
|||||||
import { Button, Group, NumberInput, Space, TextInput, Notification } from '@mantine/core';
|
import { Button, Group, NumberInput, Space, TextInput, Notification, Switch, NativeSelect } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/hooks';
|
import { useForm } from '@mantine/hooks';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { ServiceAddForm } from '../js/models';
|
import { RegexAddForm, ServiceAddForm } from '../js/models';
|
||||||
import { addservice } from '../js/utils';
|
import { addregex, addservice, b64encode, validateRegex } from '../js/utils';
|
||||||
import { ImCross } from "react-icons/im"
|
import { ImCross } from "react-icons/im"
|
||||||
|
import FilterTypeSelector from './FilterTypeSelector';
|
||||||
|
|
||||||
function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:string }) {
|
|
||||||
|
|
||||||
return <></>
|
type RegexAddInfo = {
|
||||||
/*
|
regex:string,
|
||||||
|
type:string,
|
||||||
|
mode:string,
|
||||||
|
regex_exact:boolean,
|
||||||
|
percentage_encoding:boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:string }) {
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
regex:"",
|
regex:"",
|
||||||
is_blacklist:true,
|
type:"blacklist",
|
||||||
mode:"B"
|
mode:"C <-> S",
|
||||||
|
regex_exact:false,
|
||||||
|
percentage_encoding:false
|
||||||
},
|
},
|
||||||
validationRules:{
|
validationRules:{
|
||||||
regex: (value) => value !== ""?true:false,
|
regex: (value) => value !== "" && validateRegex(value),
|
||||||
port: (value) => value>0 && value<65536
|
type: (value) => ["blacklist","whitelist"].includes(value),
|
||||||
|
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const [submitLoading, setSubmitLoading] = useState(false)
|
const [submitLoading, setSubmitLoading] = useState(false)
|
||||||
const [error, setError] = useState<string|null>(null)
|
const [error, setError] = useState<string|null>(null)
|
||||||
|
|
||||||
const submitRequest = (values:ServiceAddForm) =>{
|
const submitRequest = (values:RegexAddInfo) => {
|
||||||
setSubmitLoading(true)
|
setSubmitLoading(true)
|
||||||
addservice(values).then( res => {
|
const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode])
|
||||||
|
|
||||||
|
let final_regex = values.regex
|
||||||
|
if (values.percentage_encoding){
|
||||||
|
final_regex = decodeURIComponent(final_regex)
|
||||||
|
}
|
||||||
|
if(!values.regex_exact){
|
||||||
|
final_regex = ".*"+final_regex+".*"
|
||||||
|
}
|
||||||
|
|
||||||
|
const request:RegexAddForm = {
|
||||||
|
is_blacklist:values.type !== "whitelist",
|
||||||
|
service_id: service,
|
||||||
|
mode: filter_mode?filter_mode:"B",
|
||||||
|
regex: b64encode(final_regex)
|
||||||
|
}
|
||||||
|
setSubmitLoading(false)
|
||||||
|
addregex(request).then( res => {
|
||||||
if (!res){
|
if (!res){
|
||||||
setSubmitLoading(false)
|
setSubmitLoading(false)
|
||||||
closePopup();
|
closePopup();
|
||||||
@@ -39,32 +66,44 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
|
|||||||
setSubmitLoading(false)
|
setSubmitLoading(false)
|
||||||
setError("Request Failed! [ "+err+" ]")
|
setError("Request Failed! [ "+err+" ]")
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return <form onSubmit={form.onSubmit(submitRequest)}>
|
return <form onSubmit={form.onSubmit(submitRequest)}>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
required
|
||||||
label="Service name"
|
label="Regex"
|
||||||
placeholder="Challenge 01"
|
placeholder="[A-Z0-9]{31}="
|
||||||
{...form.getInputProps('name')}
|
{...form.getInputProps('regex')}
|
||||||
/>
|
/>
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
|
<Switch
|
||||||
<NumberInput
|
label="Use percentage encoding for binary values"
|
||||||
|
{...form.getInputProps('percentage_encoding', { type: 'checkbox' })}
|
||||||
|
/>
|
||||||
|
<Space h="md" />
|
||||||
|
<Switch
|
||||||
|
label="Match the exactly the regex"
|
||||||
|
{...form.getInputProps('regex_exact', { type: 'checkbox' })}
|
||||||
|
/>
|
||||||
|
<Space h="md" />
|
||||||
|
<NativeSelect
|
||||||
|
data={['C -> S', 'S -> C', 'C <-> S']}
|
||||||
|
label="Choose the source of the packets to filter"
|
||||||
|
variant="filled"
|
||||||
required
|
required
|
||||||
placeholder="8080"
|
{...form.getInputProps('mode')}
|
||||||
min={1}
|
|
||||||
max={65535}
|
|
||||||
label="Service port"
|
|
||||||
{...form.getInputProps('port')}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
|
<FilterTypeSelector
|
||||||
|
size="md"
|
||||||
|
color="gray"
|
||||||
|
required
|
||||||
|
{...form.getInputProps('type')}
|
||||||
|
/>
|
||||||
<Group position="right" mt="md">
|
<Group position="right" mt="md">
|
||||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
<Button loading={submitLoading} type="submit">Add Filter</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
@@ -75,7 +114,6 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
|
|||||||
</Notification><Space h="md" /></>:null}
|
</Notification><Space h="md" /></>:null}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
frontend/src/components/FilterTypeSelector.tsx
Executable file
32
frontend/src/components/FilterTypeSelector.tsx
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
import { Box, Center, SegmentedControl } from "@mantine/core";
|
||||||
|
import React from "react";
|
||||||
|
import { FaListAlt } from "react-icons/fa";
|
||||||
|
import { TiCancel } from "react-icons/ti";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function FilterTypeSelector(props:any){
|
||||||
|
return <SegmentedControl
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
value: 'blacklist',
|
||||||
|
label: (
|
||||||
|
<Center style={{color:"#FFF"}}>
|
||||||
|
<TiCancel size={23} color="red"/>
|
||||||
|
<Box ml={10}>Blacklist</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'whitelist',
|
||||||
|
label: (
|
||||||
|
<Center style={{color:"#FFF"}}>
|
||||||
|
<FaListAlt size={16} color="gray"/>
|
||||||
|
<Box ml={10}>Whitelist</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ActionIcon, Badge, Modal } from '@mantine/core';
|
import { ActionIcon, Badge, Modal } from '@mantine/core';
|
||||||
import style from "./Header.module.scss";
|
import style from "./Header.module.scss";
|
||||||
import { generalstats } from '../../js/utils';
|
import { errorNotify, generalstats } from '../../js/utils';
|
||||||
import { GeneralStats, notification_time, update_freq } from '../../js/models';
|
import { GeneralStats, update_freq } from '../../js/models';
|
||||||
import { BsPlusLg } from "react-icons/bs"
|
import { BsPlusLg } from "react-icons/bs"
|
||||||
import { AiFillHome } from "react-icons/ai"
|
import { AiFillHome } from "react-icons/ai"
|
||||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||||
import AddNewRegex from '../AddNewRegex';
|
import AddNewRegex from '../AddNewRegex';
|
||||||
import AddNewService from '../AddNewService';
|
import AddNewService from '../AddNewService';
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import { ImCross } from 'react-icons/im';
|
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
|
|
||||||
@@ -22,15 +21,8 @@ function Header() {
|
|||||||
generalstats().then(res => {
|
generalstats().then(res => {
|
||||||
setGeneralStats(res)
|
setGeneralStats(res)
|
||||||
}).catch(
|
}).catch(
|
||||||
err =>{
|
err => errorNotify("General Info Auto-Update failed!", err.toString())
|
||||||
showNotification({
|
)
|
||||||
autoClose: notification_time,
|
|
||||||
title: "General Info Auto-Update failed!",
|
|
||||||
message: "[ "+err+" ]",
|
|
||||||
color: 'red',
|
|
||||||
icon: <ImCross />,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { Center, Grid, SegmentedControl, Text, Title, Box, Badge, Space, ActionIcon } from '@mantine/core';
|
import { Center, Grid, SegmentedControl, Text, Title, Box, Badge, Space, ActionIcon } from '@mantine/core';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { RegexFilter } from '../../js/models';
|
import { RegexFilter } from '../../js/models';
|
||||||
import { getHumanReadableRegex } from '../../js/utils';
|
import { getHumanReadableRegex } from '../../js/utils';
|
||||||
import style from "./RegexView.module.scss";
|
import style from "./RegexView.module.scss";
|
||||||
import { FaListAlt } from "react-icons/fa"
|
import { FaListAlt } from "react-icons/fa"
|
||||||
import { TiCancel } from "react-icons/ti"
|
import { TiCancel } from "react-icons/ti"
|
||||||
import { BsTrashFill } from "react-icons/bs"
|
import { BsTrashFill } from "react-icons/bs"
|
||||||
|
import YesNoModal from '../YesNoModal';
|
||||||
|
import FilterTypeSelector from '../FilterTypeSelector';
|
||||||
|
|
||||||
|
|
||||||
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||||
|
|
||||||
const mode_string = regexInfo.mode == "C"? "C -> S":
|
const mode_string = regexInfo.mode === "C"? "C -> S":
|
||||||
regexInfo.mode == "S"? "S -> C":
|
regexInfo.mode === "S"? "S -> C":
|
||||||
regexInfo.mode == "B"? "S <-> C": "🤔"
|
regexInfo.mode === "B"? "S <-> C": "🤔"
|
||||||
|
|
||||||
let regex_expr = getHumanReadableRegex(regexInfo.regex);
|
let regex_expr = getHumanReadableRegex(regexInfo.regex);
|
||||||
let exact_regex = true;
|
let exact_regex = true;
|
||||||
@@ -22,6 +24,8 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
exact_regex = false;
|
exact_regex = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
|
|
||||||
return <div className={style.box}>
|
return <div className={style.box}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
@@ -31,55 +35,42 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
<Text className={style.regex_text}> {regex_expr}</Text>
|
<Text className={style.regex_text}> {regex_expr}</Text>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
<ActionIcon color="red" onClick={()=>{}} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
|
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={2} />
|
<Grid.Col span={2} />
|
||||||
<Grid.Col className='center-flex-row' span={4}>
|
<Grid.Col className='center-flex-row' span={4}>
|
||||||
<Space h="xs" />
|
<Space h="xs" />
|
||||||
<SegmentedControl
|
<FilterTypeSelector
|
||||||
data={[
|
|
||||||
{
|
|
||||||
value: 'blacklist',
|
|
||||||
label: (
|
|
||||||
<Center style={{color:"#FFF"}}>
|
|
||||||
<TiCancel size={23} color="red"/>
|
|
||||||
<Box ml={10}>Blacklist</Box>
|
|
||||||
</Center>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'whitelist',
|
|
||||||
label: (
|
|
||||||
<Center style={{color:"#FFF"}}>
|
|
||||||
<FaListAlt size={16} color="gray"/>
|
|
||||||
<Box ml={10}>Whitelist</Box>
|
|
||||||
</Center>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
size="md"
|
size="md"
|
||||||
color="gray"
|
color="gray"
|
||||||
disabled
|
disabled
|
||||||
/>
|
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
|
||||||
<Space h="md" />
|
/>
|
||||||
<div className='center-flex'>
|
<Space h="md" />
|
||||||
<Badge size="md" color="green" variant="filled">Service: {regexInfo.service_id}</Badge>
|
<div className='center-flex'>
|
||||||
<Space w="xs" />
|
<Badge size="md" color="green" variant="filled">Service: {regexInfo.service_id}</Badge>
|
||||||
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
|
<Space w="xs" />
|
||||||
</div>
|
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
|
||||||
</Grid.Col>
|
</div>
|
||||||
<Grid.Col style={{width:"100%"}} span={6}>
|
</Grid.Col>
|
||||||
|
<Grid.Col style={{width:"100%"}} span={6}>
|
||||||
|
<Space h="xs" />
|
||||||
|
<div className='center-flex-row'>
|
||||||
|
<Badge size="md" color={exact_regex?"grape":"pink"} variant="filled">Match: {exact_regex?"EXACT":"FIND"}</Badge>
|
||||||
<Space h="xs" />
|
<Space h="xs" />
|
||||||
<div className='center-flex-row'>
|
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
|
||||||
<Badge size="md" color={exact_regex?"grape":"pink"} variant="filled">Match: {exact_regex?"EXACT":"FIND"}</Badge>
|
<Space h="xs" />
|
||||||
<Space h="xs" />
|
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
|
||||||
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
|
</div>
|
||||||
<Space h="xs" />
|
</Grid.Col>
|
||||||
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
|
|
||||||
</div>
|
|
||||||
</Grid.Col>
|
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<YesNoModal
|
||||||
|
title='Are you sure to delete this regex?'
|
||||||
|
description={`You are going to delete the regex '${regex_expr}', causing the restart of the firewall if it is active.`}
|
||||||
|
onClose={()=>setDeleteModal(false)}
|
||||||
|
action={()=>console.log("Delete regex please!")}
|
||||||
|
opened={deleteModal}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { ActionIcon, Badge, Grid, Space, Title } from '@mantine/core';
|
import { ActionIcon, Badge, Grid, Space, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
|
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
|
||||||
import { Service } from '../../js/models';
|
import { Service } from '../../js/models';
|
||||||
import { MdOutlineArrowForwardIos } from "react-icons/md"
|
import { MdOutlineArrowForwardIos } from "react-icons/md"
|
||||||
import style from "./ServiceRow.module.scss";
|
import style from "./ServiceRow.module.scss";
|
||||||
|
import YesNoModal from '../YesNoModal';
|
||||||
|
|
||||||
//"status":"stop"/"wait"/"active"/"pause",
|
//"status":"stop"/"wait"/"active"/"pause",
|
||||||
function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) {
|
function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) {
|
||||||
@@ -15,6 +16,31 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
|||||||
case "active": status_color = "teal"; break;
|
case "active": status_color = "teal"; break;
|
||||||
case "pause": status_color = "cyan"; break;
|
case "pause": status_color = "cyan"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [stopModal, setStopModal] = useState(false);
|
||||||
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
|
|
||||||
|
const stopService = () => {
|
||||||
|
setButtonLoading(true)
|
||||||
|
console.log("Stop this service please!")
|
||||||
|
setButtonLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startService = () => {
|
||||||
|
setButtonLoading(true)
|
||||||
|
console.log("Start this service please!")
|
||||||
|
setButtonLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pauseService = () => {
|
||||||
|
if (service.status === "pause") return setStopModal(true)
|
||||||
|
setButtonLoading(true)
|
||||||
|
console.log("Pause this service please!")
|
||||||
|
setButtonLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Grid className={style.row} style={{width:"100%"}}>
|
<Grid className={style.row} style={{width:"100%"}}>
|
||||||
<Grid.Col span={4}>
|
<Grid.Col span={4}>
|
||||||
@@ -33,17 +59,29 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
|||||||
<Space w="xl" /><Space w="xl" />
|
<Space w="xl" /><Space w="xl" />
|
||||||
<div className="center-flex">
|
<div className="center-flex">
|
||||||
{additional_buttons}
|
{additional_buttons}
|
||||||
<ActionIcon color={service.status === "pause"?"yellow":"red"} size="xl" radius="md" variant="filled" disabled={!["wait","active","pause"].includes(service.status)?true:false}>
|
<ActionIcon color={service.status === "pause"?"yellow":"red"} loading={buttonLoading}
|
||||||
|
onClick={pauseService} size="xl" radius="md" variant="filled"
|
||||||
|
disabled={!["wait","active","pause"].includes(service.status)?true:false}>
|
||||||
{service.status === "pause"?<FaStop size="20px" />:<FaPause size="20px" />}
|
{service.status === "pause"?<FaStop size="20px" />:<FaPause size="20px" />}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<ActionIcon color="teal" size="xl" radius="md" variant="filled" disabled={!["stop","pause"].includes(service.status)?true:false}><FaPlay size="20px" /></ActionIcon>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<Space w="xl" /><Space w="xl" />
|
<Space w="xl" /><Space w="xl" />
|
||||||
{onClick?<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size="45px" />:null}
|
{onClick?<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size="45px" />:null}
|
||||||
<Space w="xl" />
|
<Space w="xl" />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<YesNoModal
|
||||||
|
title='Are you sure to stop this service!'
|
||||||
|
description={`You are going to delete the service '${service.id}', causing the stopping of the firewall. This will cause the shutdown of your service ⚠️!`}
|
||||||
|
onClose={()=>setStopModal(false)}
|
||||||
|
action={stopService}
|
||||||
|
opened={stopModal}
|
||||||
|
/>
|
||||||
<hr style={{width:"100%"}}/>
|
<hr style={{width:"100%"}}/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
19
frontend/src/components/YesNoModal.tsx
Executable file
19
frontend/src/components/YesNoModal.tsx
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Button, Group, Modal } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function YesNoModal( { title, description, action, onClose, opened}:{ title:string, description:string, onClose:()=>void, action:()=>void, opened:boolean} ){
|
||||||
|
|
||||||
|
return <Modal size="xl" title={title} opened={opened} onClose={onClose} centered>
|
||||||
|
{description}
|
||||||
|
<Group position="right" mt="md">
|
||||||
|
<Button onClick={()=>{
|
||||||
|
onClose()
|
||||||
|
action()
|
||||||
|
}} color="teal" type="submit">Yes</Button>
|
||||||
|
<Button onClick={onClose} color="red" type="submit">No</Button>
|
||||||
|
|
||||||
|
</Group>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default YesNoModal;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
export const update_freq = 5000;
|
export const update_freq = 3000;
|
||||||
export const notification_time = 2000;
|
export const notification_time = 2000;
|
||||||
|
|
||||||
export type GeneralStats = {
|
export type GeneralStats = {
|
||||||
@@ -36,4 +36,11 @@ export type RegexFilter = {
|
|||||||
is_blacklist:boolean,
|
is_blacklist:boolean,
|
||||||
mode:string //C S B => C->S S->C BOTH
|
mode:string //C S B => C->S S->C BOTH
|
||||||
n_packets:number
|
n_packets:number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RegexAddForm = {
|
||||||
|
"service_id":string,
|
||||||
|
"regex":string,
|
||||||
|
"is_blacklist":boolean,
|
||||||
|
"mode":string // C->S S->C BOTH
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter } from "./models";
|
import { showNotification } from "@mantine/notifications";
|
||||||
|
import { ImCross } from "react-icons/im";
|
||||||
|
import { TiTick } from "react-icons/ti"
|
||||||
|
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, notification_time, RegexAddForm } from "./models";
|
||||||
|
|
||||||
|
var Buffer = require('buffer').Buffer
|
||||||
|
|
||||||
export async function getapi(path:string):Promise<any>{
|
export async function getapi(path:string):Promise<any>{
|
||||||
return await fetch(`/api/${path}`).then( res => res.json() )
|
return await fetch(`/api/${path}`).then( res => res.json() )
|
||||||
@@ -33,6 +38,11 @@ export async function addservice(data:ServiceAddForm) {
|
|||||||
return status === "ok"?undefined:status
|
return status === "ok"?undefined:status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addregex(data:RegexAddForm) {
|
||||||
|
const { status } = await postapi("regexes/add",data) as ServerResponse;
|
||||||
|
return status === "ok"?undefined:status
|
||||||
|
}
|
||||||
|
|
||||||
export async function serviceregexlist(service_id:string){
|
export async function serviceregexlist(service_id:string){
|
||||||
return await getapi(`service/${service_id}/regexes`) as RegexFilter[];
|
return await getapi(`service/${service_id}/regexes`) as RegexFilter[];
|
||||||
}
|
}
|
||||||
@@ -40,7 +50,6 @@ export async function serviceregexlist(service_id:string){
|
|||||||
const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&\'()*+,-./:;<=>?@[\\]^_`{|}~ ";
|
const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&\'()*+,-./:;<=>?@[\\]^_`{|}~ ";
|
||||||
|
|
||||||
export function getHumanReadableRegex(regexB64:string){
|
export function getHumanReadableRegex(regexB64:string){
|
||||||
var Buffer = require('buffer').Buffer
|
|
||||||
const regex = Buffer.from(regexB64, "base64")
|
const regex = Buffer.from(regexB64, "base64")
|
||||||
let res = ""
|
let res = ""
|
||||||
for (let i=0; i < regex.length; i++){
|
for (let i=0; i < regex.length; i++){
|
||||||
@@ -52,4 +61,45 @@ export function getHumanReadableRegex(regexB64:string){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function errorNotify(title:string, description:string ){
|
||||||
|
showNotification({
|
||||||
|
autoClose: notification_time,
|
||||||
|
title: title,
|
||||||
|
message: description,
|
||||||
|
color: 'red',
|
||||||
|
icon: <ImCross />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function okNotify(title:string, description:string ){
|
||||||
|
showNotification({
|
||||||
|
autoClose: notification_time,
|
||||||
|
title: title,
|
||||||
|
message: description,
|
||||||
|
color: 'teal',
|
||||||
|
icon: <TiTick />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateRegex(pattern:string) {
|
||||||
|
var parts = pattern.split('/'),
|
||||||
|
regex = pattern,
|
||||||
|
options = "";
|
||||||
|
if (parts.length > 1) {
|
||||||
|
regex = parts[1];
|
||||||
|
options = parts[2];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new RegExp(regex, options);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function b64encode(data:string){
|
||||||
|
return Buffer.from(data).toString('base64')
|
||||||
}
|
}
|
||||||
@@ -1,50 +1,22 @@
|
|||||||
import { Space, Title } from '@mantine/core';
|
import { Space, Title } from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import ServiceRow from '../components/ServiceRow';
|
import ServiceRow from '../components/ServiceRow';
|
||||||
import { notification_time, Service, update_freq } from '../js/models';
|
import { Service, update_freq } from '../js/models';
|
||||||
import { servicelist } from '../js/utils';
|
import { errorNotify, servicelist } from '../js/utils';
|
||||||
import { ImCross } from "react-icons/im"
|
|
||||||
|
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
|
|
||||||
const [services, setServices] = useState<Service[]>([
|
const [services, setServices] = useState<Service[]>([]);
|
||||||
{
|
|
||||||
id:"ctfe",
|
|
||||||
internal_port:18080,
|
|
||||||
n_packets: 30,
|
|
||||||
n_regex: 40,
|
|
||||||
name:"CTFe",
|
|
||||||
public_port:80,
|
|
||||||
status:"pause"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id:"saas",
|
|
||||||
internal_port:18080,
|
|
||||||
n_packets: 30,
|
|
||||||
n_regex: 40,
|
|
||||||
name:"SaaS",
|
|
||||||
public_port:5000,
|
|
||||||
status:"active"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
const navigator = useNavigate()
|
const navigator = useNavigate()
|
||||||
|
|
||||||
const updateInfo = () => {
|
const updateInfo = () => {
|
||||||
servicelist().then(res => {
|
servicelist().then(res => {
|
||||||
setServices(res)
|
setServices(res)
|
||||||
}).catch(
|
}).catch(
|
||||||
err =>{
|
err => errorNotify("Home Page Auto-Update failed!", err.toString())
|
||||||
showNotification({
|
)
|
||||||
autoClose: notification_time,
|
|
||||||
title: "Home Page Auto-Update failed!",
|
|
||||||
message: "[ "+err+" ]",
|
|
||||||
color: 'red',
|
|
||||||
icon: <ImCross />,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { ActionIcon, Grid, Space, Title } from '@mantine/core';
|
import { ActionIcon, Grid, Space, Title } from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { BsTrashFill } from 'react-icons/bs';
|
import { BsTrashFill } from 'react-icons/bs';
|
||||||
import { ImCross } from 'react-icons/im';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import RegexView from '../components/RegexView';
|
import RegexView from '../components/RegexView';
|
||||||
import ServiceRow from '../components/ServiceRow';
|
import ServiceRow from '../components/ServiceRow';
|
||||||
import { notification_time, RegexFilter, Service, update_freq } from '../js/models';
|
import YesNoModal from '../components/YesNoModal';
|
||||||
import { serviceinfo, serviceregexlist } from '../js/utils';
|
import { RegexFilter, Service, update_freq } from '../js/models';
|
||||||
|
import { errorNotify, serviceinfo, serviceregexlist } from '../js/utils';
|
||||||
|
|
||||||
function ServiceDetails() {
|
function ServiceDetails() {
|
||||||
const {srv_id} = useParams()
|
const {srv_id} = useParams()
|
||||||
@@ -22,24 +21,9 @@ function ServiceDetails() {
|
|||||||
status:"🤔"
|
status:"🤔"
|
||||||
})
|
})
|
||||||
|
|
||||||
const [regexesList, setRegexesList] = useState<RegexFilter[]>([
|
const [regexesList, setRegexesList] = useState<RegexFilter[]>([])
|
||||||
{
|
|
||||||
id:3546,
|
const navigator = useNavigate()
|
||||||
is_blacklist:true,
|
|
||||||
mode:"B",
|
|
||||||
regex:"LipmbGFnX2NoZWNrLio=",
|
|
||||||
service_id:"ctfe",
|
|
||||||
n_packets:5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id:3546,
|
|
||||||
is_blacklist:true,
|
|
||||||
mode:"B",
|
|
||||||
regex:"d2VkcmZoaXdlZGZoYnVp",
|
|
||||||
service_id:"ctfe",
|
|
||||||
n_packets: 54
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const updateInfo = async () => {
|
const updateInfo = async () => {
|
||||||
if (!srv_id) return
|
if (!srv_id) return
|
||||||
@@ -48,28 +32,17 @@ function ServiceDetails() {
|
|||||||
setServiceInfo(res)
|
setServiceInfo(res)
|
||||||
}).catch(
|
}).catch(
|
||||||
err =>{
|
err =>{
|
||||||
showNotification({
|
errorNotify(`Updater for ${srv_id} service failed [General Info]!`, err.toString())
|
||||||
autoClose: notification_time,
|
error = true;
|
||||||
title: `Updater for ${srv_id} service failed [General Info]!`,
|
navigator("/")
|
||||||
message: "[ "+err+" ]",
|
|
||||||
color: 'red',
|
|
||||||
icon: <ImCross />,
|
|
||||||
});
|
|
||||||
error = true;
|
|
||||||
})
|
})
|
||||||
if (error) return
|
if (error) return
|
||||||
await serviceregexlist(srv_id).then(res => {
|
await serviceregexlist(srv_id).then(res => {
|
||||||
setRegexesList(res)
|
setRegexesList(res)
|
||||||
}).catch(
|
}).catch(
|
||||||
err =>{
|
err =>{
|
||||||
showNotification({
|
errorNotify(`Updater for ${srv_id} service failed [Regex list]!`, err.toString())
|
||||||
autoClose: notification_time,
|
error = true;
|
||||||
title: `Updater for ${srv_id} service failed [Regex list]!`,
|
|
||||||
message: "[ "+err+" ]",
|
|
||||||
color: 'red',
|
|
||||||
icon: <ImCross />,
|
|
||||||
});
|
|
||||||
error = true;
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +51,12 @@ function ServiceDetails() {
|
|||||||
const updater = setInterval(updateInfo, update_freq)
|
const updater = setInterval(updateInfo, update_freq)
|
||||||
return () => { clearInterval(updater) }
|
return () => { clearInterval(updater) }
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<ServiceRow service={serviceInfo} additional_buttons={<>
|
<ServiceRow service={serviceInfo} additional_buttons={<>
|
||||||
<ActionIcon color="red" onClick={()=>{}} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
|
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
</>}></ServiceRow>
|
</>}></ServiceRow>
|
||||||
{regexesList.length === 0?
|
{regexesList.length === 0?
|
||||||
@@ -90,6 +65,13 @@ function ServiceDetails() {
|
|||||||
{regexesList.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={6}><RegexView regexInfo={regexInfo}/></Grid.Col>)}
|
{regexesList.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={6}><RegexView regexInfo={regexInfo}/></Grid.Col>)}
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
}
|
||||||
|
<YesNoModal
|
||||||
|
title='Are you sure to delete this service?'
|
||||||
|
description={`You are going to delete the service '${serviceInfo.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={()=>console.log("Delete the service please!")}
|
||||||
|
opened={deleteModal}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user