Restructuring docker project

This commit is contained in:
DomySh
2022-06-12 22:28:16 +02:00
parent 67e5014d78
commit cff484a976
58 changed files with 357 additions and 407 deletions

17
frontend/src/App.tsx Executable file
View File

@@ -0,0 +1,17 @@
import React from 'react';
import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
import MainLayout from './components/MainLayout';
import HomePage from './pages/HomePage';
import ServiceDetails from './pages/ServiceDetails';
function App() {
return <Routes>
<Route element={<MainLayout><Outlet /></MainLayout>}>
<Route index element={<HomePage />} />
<Route path=":srv_id" element={<ServiceDetails />} />
<Route path="*" element={<Navigate to="/" />} />
</Route>
</Routes>
}
export default App;

4
frontend/src/_vars.scss Executable file
View File

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

View File

@@ -0,0 +1,120 @@
import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useState } from 'react';
import { RegexAddForm } from '../js/models';
import { addregex, b64encode, validateRegex } from '../js/utils';
import { ImCross } from "react-icons/im"
import FilterTypeSelector from './FilterTypeSelector';
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({
initialValues: {
regex:"",
type:"blacklist",
mode:"C <-> S",
regex_exact:false,
percentage_encoding:false
},
validationRules:{
regex: (value) => value !== "" && validateRegex(value),
type: (value) => ["blacklist","whitelist"].includes(value),
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
}
})
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])
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){
setSubmitLoading(false)
closePopup();
}else{
setSubmitLoading(false)
setError("Invalid request! [ "+res+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <form onSubmit={form.onSubmit(submitRequest)}>
<TextInput
required
label="Regex"
placeholder="[A-Z0-9]{31}="
{...form.getInputProps('regex')}
/>
<Space h="md" />
<Switch
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
{...form.getInputProps('mode')}
/>
<Space h="md" />
<FilterTypeSelector
size="md"
color="gray"
required
{...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>
}
export default AddNewRegex;

View File

@@ -0,0 +1,77 @@
import { Button, Group, NumberInput, Space, TextInput, Notification } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useState } from 'react';
import { ServiceAddForm } from '../js/models';
import { addservice } from '../js/utils';
import { ImCross } from "react-icons/im"
function AddNewService({ closePopup }:{ closePopup:()=>void }) {
const form = useForm({
initialValues: {
name:"",
port:1,
},
validationRules:{
name: (value) => value !== ""?true:false,
port: (value) => value>0 && value<65536
}
})
const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null)
const submitRequest = (values:ServiceAddForm) =>{
setSubmitLoading(true)
addservice(values).then( res => {
if (!res){
setSubmitLoading(false)
closePopup();
}else{
setSubmitLoading(false)
setError("Invalid request! [ "+res+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <form onSubmit={form.onSubmit(submitRequest)}>
<TextInput
required
label="Service name"
placeholder="Challenge 01"
{...form.getInputProps('name')}
/>
<Space h="md" />
<NumberInput
required
placeholder="8080"
min={1}
max={65535}
label="Service port"
{...form.getInputProps('port')}
/>
<Space h="md" />
<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>
}
export default AddNewService;

View 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}
/>
}

View File

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

View File

@@ -0,0 +1,12 @@
import React from 'react';
import style from "./Footer.module.scss";
function Footer() {
return <div id="footer" className={style.footer}>
Made by Pwnzer0tt1
</div>
}
export default Footer;

View File

@@ -0,0 +1,17 @@
@use "../../vars" 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%;
}

View File

@@ -0,0 +1,70 @@
import React, { useEffect, useState } from 'react';
import { ActionIcon, Badge, Image, Modal } from '@mantine/core';
import style from "./Header.module.scss";
import { errorNotify, generalstats } from '../../js/utils';
import { GeneralStats, update_freq } from '../../js/models';
import { BsPlusLg } from "react-icons/bs"
import { AiFillHome } from "react-icons/ai"
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import AddNewRegex from '../AddNewRegex';
import AddNewService from '../AddNewService';
function Header() {
const [generalStats, setGeneralStats] = useState<GeneralStats>({closed:0, regexes:0, services:0});
const location = useLocation()
const navigator = useNavigate()
const updateInfo = () => {
generalstats().then(res => {
setGeneralStats(res)
}).catch(
err => errorNotify("General Info Auto-Update failed!", err.toString())
)
}
useEffect(()=>{
updateInfo()
const updater = setInterval(updateInfo, update_freq)
return () => { clearInterval(updater) }
}, []);
const {srv_id} = 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" }}>
<Image src="/header-logo.png" alt="Firegex logo" />
</div>
<div className="flex-spacer" />
<Badge color="green" size="lg" variant="filled">Services: {generalStats.services}</Badge>
<Badge style={{marginLeft:"10px"}} size="lg" color="yellow" variant="filled">Filtered Connections: {generalStats.closed}</Badge>
<Badge style={{marginLeft:"10px"}} size="lg" color="violet" variant="filled">Regexes: {generalStats.regexes}</Badge>
<div style={{marginLeft:"20px"}}></div>
{ location.pathname !== "/"?
<ActionIcon color="teal" style={{marginRight:"10px"}}
size="xl" radius="md" variant="filled"
onClick={()=>navigator("/")}>
<AiFillHome size="25px" />
</ActionIcon>
:null}
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"><BsPlusLg size="20px" /></ActionIcon>
{srv_id?
<Modal size="xl" title="Add a new regex filter" opened={open} onClose={closeModal} closeOnClickOutside={false} centered>
<AddNewRegex closePopup={closeModal} service={srv_id} />
</Modal>:
<Modal size="xl" title="Add a new service" opened={open} onClose={closeModal} closeOnClickOutside={false} centered>
<AddNewService closePopup={closeModal} />
</Modal>
}
<div style={{marginLeft:"40px"}}></div>
</div>
}
export default Header;

View File

@@ -0,0 +1,23 @@
import { Container, MantineProvider, Space } from '@mantine/core';
import { NotificationsProvider } from '@mantine/notifications';
import React from 'react';
import Footer from './Footer';
import Header from './Header';
function MainLayout({ children }:{ children:any }) {
return <>
<MantineProvider theme={{ colorScheme: 'dark' }} withGlobalStyles withNormalizeCSS>
<NotificationsProvider>
<Header />
<Space h="xl" />
<Container size="xl" style={{minHeight:"57.5vh"}}>
{children}
</Container>
<Space h="xl" />
<Footer />
</NotificationsProvider>
</MantineProvider>
</>
}
export default MainLayout;

View File

@@ -0,0 +1,13 @@
@use "../../vars" as *;
.box{
padding:30px;
margin:5px;
}
.regex_text{
padding: 10px;
background-color: $third_color;
border-radius: 15px;
}

View File

@@ -0,0 +1,76 @@
import { Grid, Text, Title, Badge, Space, ActionIcon } from '@mantine/core';
import React, { useState } from 'react';
import { RegexFilter } from '../../js/models';
import { getHumanReadableRegex } from '../../js/utils';
import style from "./RegexView.module.scss";
import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal';
import FilterTypeSelector from '../FilterTypeSelector';
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 = getHumanReadableRegex(regexInfo.regex);
let exact_regex = true;
if (regex_expr.length>=4 && regex_expr.startsWith(".*") && regex_expr.endsWith(".*")){
regex_expr = regex_expr.substring(2,regex_expr.length-2)
exact_regex = false;
}
const [deleteModal, setDeleteModal] = useState(false);
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}>
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
</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="green" variant="filled">Service: {regexInfo.service_id}</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={exact_regex?"grape":"pink"} variant="filled">Match: {exact_regex?"EXACT":"FIND"}</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}', causing the restart of the firewall if it is active.`}
onClose={()=>setDeleteModal(false)}
action={()=>console.log("Delete regex please!")}
opened={deleteModal}
/>
</div>
}
export default RegexView;

View 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;
}

View File

@@ -0,0 +1,89 @@
import { ActionIcon, Badge, Grid, Space, Title } 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';
//"status":"stop"/"wait"/"active"/"pause",
function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) {
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 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 <>
<Grid className={style.row} style={{width:"100%"}}>
<Grid.Col span={4}>
<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>
</Grid.Col>
<Grid.Col className="center-flex" span={8}>
<div className='flex-spacer'></div>
<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>
<Space w="xl" /><Space w="xl" />
<div className="center-flex">
{additional_buttons}
<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" />}
</ActionIcon>
<Space w="md"/>
<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>
<Space w="xl" /><Space w="xl" />
{onClick?<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size="45px" />:null}
<Space w="xl" />
</Grid.Col>
</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%"}}/>
</>
}
export default ServiceRow;

View 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;

35
frontend/src/index.scss Executable file
View File

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

14
frontend/src/index.tsx Executable file
View File

@@ -0,0 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom"
import './index.scss';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);

45
frontend/src/js/models.ts Executable file
View File

@@ -0,0 +1,45 @@
export const update_freq = 3000;
export const notification_time = 2000;
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
}
export type ServerResponse = {
status:string
}
export type RegexFilter = {
id:number,
service_id:string,
regex:string
is_blacklist:boolean,
mode:string //C S B => C->S S->C BOTH
n_packets:number
}
export type RegexAddForm = {
"service_id":string,
"regex":string,
"is_blacklist":boolean,
"mode":string // C->S S->C BOTH
}

105
frontend/src/js/utils.tsx Executable file
View File

@@ -0,0 +1,105 @@
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>{
return await fetch(`/api/${path}`).then( res => res.json() )
}
export async function postapi(path:string,data:any):Promise<any>{
return await fetch(`/api/${path}`, {
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(res => res.json());
}
export async function generalstats(){
return await getapi("general-stats") as GeneralStats;
}
export async function servicelist(){
return await getapi("services") as Service[];
}
export async function serviceinfo(service_id:string){
return await getapi(`service/${service_id}`) as Service;
}
export async function addservice(data:ServiceAddForm) {
const { status } = await postapi("services/add",data) as ServerResponse;
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){
return await getapi(`service/${service_id}/regexes`) as RegexFilter[];
}
const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
export function getHumanReadableRegex(regexB64:string){
const regex = Buffer.from(regexB64, "base64")
let res = ""
for (let i=0; i < regex.length; i++){
const byte = String.fromCharCode(regex[i]);
if (unescapedChars.includes(byte)){
res+=byte
}else{
res+="%"+regex[i].toString(16)
}
}
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')
}

35
frontend/src/pages/HomePage.tsx Executable file
View File

@@ -0,0 +1,35 @@
import { Space, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import ServiceRow from '../components/ServiceRow';
import { Service, update_freq } from '../js/models';
import { errorNotify, servicelist } from '../js/utils';
function HomePage() {
const [services, setServices] = useState<Service[]>([]);
const navigator = useNavigate()
const updateInfo = () => {
servicelist().then(res => {
setServices(res)
}).catch(
err => errorNotify("Home Page Auto-Update failed!", err.toString())
)
}
useEffect(()=>{
updateInfo()
const updater = setInterval(updateInfo, update_freq)
return () => { clearInterval(updater) }
}, []);
return <div id="service-list" className="center-flex-row">
{services.length > 0?services.map( srv => <ServiceRow service={srv} key={srv.id} onClick={()=>{
navigator("/"+srv.id)
}} />):<><Space h="xl"/> <Title className='center-flex' order={3}>No services found! Add one clicking the button above</Title></>}
</div>
}
export default HomePage;

View File

@@ -0,0 +1,75 @@
import { ActionIcon, Grid, Space, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { BsTrashFill } from 'react-icons/bs';
import { useNavigate, useParams } from 'react-router-dom';
import RegexView from '../components/RegexView';
import ServiceRow from '../components/ServiceRow';
import YesNoModal from '../components/YesNoModal';
import { RegexFilter, Service, update_freq } from '../js/models';
import { errorNotify, serviceinfo, serviceregexlist } from '../js/utils';
function ServiceDetails() {
const {srv_id} = useParams()
const [serviceInfo, setServiceInfo] = useState<Service>({
id:srv_id?srv_id:"",
internal_port:0,
n_packets:0,
n_regex:0,
name:srv_id?srv_id:"",
public_port:0,
status:"🤔"
})
const [regexesList, setRegexesList] = useState<RegexFilter[]>([])
const navigator = useNavigate()
const updateInfo = async () => {
if (!srv_id) return
let error = false;
await serviceinfo(srv_id).then(res => {
setServiceInfo(res)
}).catch(
err =>{
error = true;
navigator("/")
})
if (error) return
await serviceregexlist(srv_id).then(res => {
setRegexesList(res)
}).catch(
err => errorNotify(`Updater for ${srv_id} service failed [Regex list]!`, err.toString())
)
}
useEffect(()=>{
updateInfo()
const updater = setInterval(updateInfo, update_freq)
return () => { clearInterval(updater) }
});
const [deleteModal, setDeleteModal] = useState(false)
return <>
<ServiceRow service={serviceInfo} additional_buttons={<>
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
<Space w="md"/>
</>}></ServiceRow>
{regexesList.length === 0?
<><Space h="xl" /> <Title className='center-flex' order={3}>No regex found for this service! Add one clicking the add button above</Title></>:
<Grid>
{regexesList.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={6}><RegexView regexInfo={regexInfo}/></Grid.Col>)}
</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}
/>
</>
}
export default ServiceDetails;

1
frontend/src/react-app-env.d.ts vendored Executable file
View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />