Improve stability and functionalities
This commit is contained in:
@@ -36,8 +36,9 @@ def JWT_SECRET(): return conf.get("secret")
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
await firewall.close()
|
||||
|
||||
db.disconnect()
|
||||
await firewall.close()
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
@@ -197,7 +198,7 @@ async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)
|
||||
return db.query("""
|
||||
SELECT
|
||||
regex, mode, regex_id `id`, service_id, is_blacklist,
|
||||
blocked_packets n_packets, is_case_sensitive
|
||||
blocked_packets n_packets, is_case_sensitive, active
|
||||
FROM regexes WHERE service_id = ?;
|
||||
""", service_id)
|
||||
|
||||
@@ -206,7 +207,7 @@ async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)):
|
||||
res = db.query("""
|
||||
SELECT
|
||||
regex, mode, regex_id `id`, service_id, is_blacklist,
|
||||
blocked_packets n_packets, is_case_sensitive
|
||||
blocked_packets n_packets, is_case_sensitive, active
|
||||
FROM regexes WHERE `id` = ?;
|
||||
""", regex_id)
|
||||
if len(res) == 0: raise HTTPException(status_code=400, detail="This regex does not exists!")
|
||||
@@ -221,6 +222,22 @@ async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
|
||||
|
||||
return {'status': 'ok'}
|
||||
|
||||
@app.get('/api/regex/{regex_id}/enable')
|
||||
async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
|
||||
res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id)
|
||||
if len(res) != 0:
|
||||
db.query('UPDATE regexes SET active=1 WHERE regex_id = ?;', regex_id)
|
||||
await firewall.get(res[0]["service_id"]).update_filters()
|
||||
return {'status': 'ok'}
|
||||
|
||||
@app.get('/api/regex/{regex_id}/disable')
|
||||
async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
|
||||
res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id)
|
||||
if len(res) != 0:
|
||||
db.query('UPDATE regexes SET active=0 WHERE regex_id = ?;', regex_id)
|
||||
await firewall.get(res[0]["service_id"]).update_filters()
|
||||
return {'status': 'ok'}
|
||||
|
||||
class RegexAddForm(BaseModel):
|
||||
service_id: str
|
||||
regex: str
|
||||
@@ -261,7 +278,7 @@ async def post_services_add(form: ServiceAddForm, auth: bool = Depends(is_loggin
|
||||
|
||||
async def frontend_debug_proxy(path):
|
||||
httpc = httpx.AsyncClient()
|
||||
req = httpc.build_request("GET",urllib.parse.urljoin(f"http://0.0.0.0:{os.getenv('F_PORT','3000')}", path))
|
||||
req = httpc.build_request("GET",urllib.parse.urljoin(f"http://127.0.0.1:{os.getenv('F_PORT','3000')}", path))
|
||||
resp = await httpc.send(req, stream=True)
|
||||
return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code)
|
||||
|
||||
@@ -287,7 +304,7 @@ if DEBUG:
|
||||
@app.websocket("/ws")
|
||||
async def websocket_debug_proxy(ws: WebSocket):
|
||||
await ws.accept()
|
||||
async with websockets.connect(f"ws://0.0.0.0:{os.getenv('F_PORT','3000')}/ws") as ws_b_client:
|
||||
async with websockets.connect(f"ws://127.0.0.1:{os.getenv('F_PORT','3000')}/ws") as ws_b_client:
|
||||
fwd_task = asyncio.create_task(forward_websocket(ws, ws_b_client))
|
||||
rev_task = asyncio.create_task(reverse_websocket(ws, ws_b_client))
|
||||
await asyncio.gather(fwd_task, rev_task)
|
||||
@@ -298,7 +315,7 @@ async def catch_all(full_path:str):
|
||||
try:
|
||||
return await frontend_debug_proxy(full_path)
|
||||
except Exception:
|
||||
return {"details":"Frontend not started at "+f"http://0.0.0.0:{os.getenv('F_PORT','3000')}"}
|
||||
return {"details":"Frontend not started at "+f"http://127.0.0.1:{os.getenv('F_PORT','3000')}"}
|
||||
else: return await react_deploy(full_path)
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class Proxy:
|
||||
if not self.isactive():
|
||||
try:
|
||||
self.filter_map = self.compile_filters()
|
||||
filters_codes = list(self.filter_map.keys()) if not in_pause else []
|
||||
filters_codes = self.get_filter_codes() if not in_pause else []
|
||||
proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"./proxy")
|
||||
|
||||
self.process = await asyncio.create_subprocess_exec(
|
||||
@@ -57,8 +57,9 @@ class Proxy:
|
||||
if stdout_line.startswith("BLOCKED"):
|
||||
regex_id = stdout_line.split()[1]
|
||||
async with self.filter_map_lock:
|
||||
self.filter_map[regex_id].blocked+=1
|
||||
if self.callback_blocked_update: self.callback_blocked_update(self.filter_map[regex_id])
|
||||
if regex_id in self.filter_map:
|
||||
self.filter_map[regex_id].blocked+=1
|
||||
if self.callback_blocked_update: self.callback_blocked_update(self.filter_map[regex_id])
|
||||
except Exception:
|
||||
return await self.process.wait()
|
||||
else:
|
||||
@@ -87,9 +88,14 @@ class Proxy:
|
||||
if self.isactive():
|
||||
async with self.filter_map_lock:
|
||||
self.filter_map = self.compile_filters()
|
||||
filters_codes = list(self.filter_map.keys())
|
||||
filters_codes = self.get_filter_codes()
|
||||
await self.update_config(filters_codes)
|
||||
|
||||
def get_filter_codes(self):
|
||||
filters_codes = list(self.filter_map.keys())
|
||||
filters_codes.sort(key=lambda a: self.filter_map[a].blocked, reverse=True)
|
||||
return filters_codes
|
||||
|
||||
def isactive(self):
|
||||
return self.process and self.process.returncode is None
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ class SQLite():
|
||||
'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0',
|
||||
'regex_id': 'INTEGER PRIMARY KEY',
|
||||
'is_case_sensitive' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1))',
|
||||
'active' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1)) DEFAULT 1',
|
||||
'FOREIGN KEY (service_id)':'REFERENCES services (service_id)',
|
||||
},
|
||||
'keys_values': {
|
||||
@@ -109,6 +110,7 @@ class ServiceManager:
|
||||
callback_blocked_update=self._stats_updater
|
||||
)
|
||||
self.status = STATUS.STOP
|
||||
self.wanted_status = STATUS.STOP
|
||||
self.filters = {}
|
||||
self._update_port_from_db()
|
||||
self._update_filters_from_db()
|
||||
@@ -131,7 +133,7 @@ class ServiceManager:
|
||||
SELECT
|
||||
regex, mode, regex_id `id`, is_blacklist,
|
||||
blocked_packets n_packets, is_case_sensitive
|
||||
FROM regexes WHERE service_id = ?;
|
||||
FROM regexes WHERE service_id = ? AND active=1;
|
||||
""", self.id)
|
||||
|
||||
#Filter check
|
||||
@@ -162,26 +164,30 @@ class ServiceManager:
|
||||
|
||||
async def next(self,to):
|
||||
async with self.lock:
|
||||
if self.status != to:
|
||||
# ACTIVE -> PAUSE or PAUSE -> ACTIVE
|
||||
if (self.status, to) in [(STATUS.ACTIVE, STATUS.PAUSE)]:
|
||||
await self.proxy.pause()
|
||||
self._set_status(to)
|
||||
return await self._next(to)
|
||||
|
||||
elif (self.status, to) in [(STATUS.PAUSE, STATUS.ACTIVE)]:
|
||||
await self.proxy.reload()
|
||||
self._set_status(to)
|
||||
async def _next(self, to):
|
||||
if self.status != to:
|
||||
# ACTIVE -> PAUSE or PAUSE -> ACTIVE
|
||||
if (self.status, to) in [(STATUS.ACTIVE, STATUS.PAUSE)]:
|
||||
await self.proxy.pause()
|
||||
self._set_status(to)
|
||||
|
||||
# ACTIVE -> STOP
|
||||
elif (self.status,to) in [(STATUS.ACTIVE, STATUS.STOP), (STATUS.WAIT, STATUS.STOP), (STATUS.PAUSE, STATUS.STOP)]: #Stop proxy
|
||||
if self.starter: self.starter.cancel()
|
||||
await self.proxy.stop()
|
||||
self._set_status(to)
|
||||
elif (self.status, to) in [(STATUS.PAUSE, STATUS.ACTIVE)]:
|
||||
await self.proxy.reload()
|
||||
self._set_status(to)
|
||||
|
||||
# STOP -> ACTIVE or STOP -> PAUSE
|
||||
elif (self.status, to) in [(STATUS.STOP, STATUS.ACTIVE), (STATUS.STOP, STATUS.PAUSE)]:
|
||||
self._set_status(STATUS.WAIT)
|
||||
self.__proxy_starter(to)
|
||||
# ACTIVE -> STOP
|
||||
elif (self.status,to) in [(STATUS.ACTIVE, STATUS.STOP), (STATUS.WAIT, STATUS.STOP), (STATUS.PAUSE, STATUS.STOP)]: #Stop proxy
|
||||
if self.starter: self.starter.cancel()
|
||||
await self.proxy.stop()
|
||||
self._set_status(to)
|
||||
|
||||
# STOP -> ACTIVE or STOP -> PAUSE
|
||||
elif (self.status, to) in [(STATUS.STOP, STATUS.ACTIVE), (STATUS.STOP, STATUS.PAUSE)]:
|
||||
self.wanted_status = to
|
||||
self._set_status(STATUS.WAIT)
|
||||
self.__proxy_starter(to)
|
||||
|
||||
|
||||
def _stats_updater(self,filter:Filter):
|
||||
@@ -191,7 +197,9 @@ class ServiceManager:
|
||||
async with self.lock:
|
||||
self._update_port_from_db()
|
||||
if self.status in [STATUS.PAUSE, STATUS.ACTIVE]:
|
||||
await self.proxy.restart(in_pause=(self.status == STATUS.PAUSE))
|
||||
next_status = self.status if self.status != STATUS.WAIT else self.wanted_status
|
||||
await self._next(STATUS.STOP)
|
||||
await self._next(next_status)
|
||||
|
||||
def _set_status(self,status):
|
||||
self.status = status
|
||||
@@ -233,7 +241,7 @@ class ProxyManager:
|
||||
async def remove(self,id):
|
||||
async with self.lock:
|
||||
if id in self.proxy_table:
|
||||
await self.proxy_table[id].proxy.stop()
|
||||
await self.proxy_table[id].next(STATUS.STOP)
|
||||
del self.proxy_table[id]
|
||||
|
||||
async def reload(self):
|
||||
|
||||
BIN
db/firegex.db
Normal file
BIN
db/firegex.db
Normal file
Binary file not shown.
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.c375ae17.css",
|
||||
"main.js": "/static/js/main.cd9ce6a2.js",
|
||||
"main.js": "/static/js/main.a6ace121.js",
|
||||
"index.html": "/index.html",
|
||||
"main.c375ae17.css.map": "/static/css/main.c375ae17.css.map",
|
||||
"main.cd9ce6a2.js.map": "/static/js/main.cd9ce6a2.js.map"
|
||||
"main.a6ace121.js.map": "/static/js/main.a6ace121.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.c375ae17.css",
|
||||
"static/js/main.cd9ce6a2.js"
|
||||
"static/js/main.a6ace121.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.cd9ce6a2.js"></script><link href="/static/css/main.c375ae17.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.a6ace121.js"></script><link href="/static/css/main.c375ae17.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
1
frontend/build/static/js/main.a6ace121.js.map
Executable file
1
frontend/build/static/js/main.a6ace121.js.map
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -15,12 +15,15 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name:"",
|
||||
port:1,
|
||||
port:8080,
|
||||
internalPort:30001,
|
||||
chosenInternalPort:false,
|
||||
autostart: true
|
||||
},
|
||||
validationRules:{
|
||||
name: (value) => value !== ""?true:false,
|
||||
port: (value) => value>0 && value<65536
|
||||
port: (value) => value>0 && value<65536,
|
||||
internalPort: (value) => value>0 && value<65536,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -66,15 +69,34 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
placeholder="8080"
|
||||
min={1}
|
||||
max={65535}
|
||||
label="Service port"
|
||||
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="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
label="Choose internal port"
|
||||
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
|
||||
/>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import { RegexFilter } from '../../js/models';
|
||||
import { deleteregex, errorNotify, fireUpdateRequest, getHumanReadableRegex, okNotify } from '../../js/utils';
|
||||
import { activateregex, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, getHumanReadableRegex, 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 }) {
|
||||
@@ -17,7 +18,8 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
let regex_expr = getHumanReadableRegex(regexInfo.regex);
|
||||
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [tooltipOpened, setTooltipOpened] = useState(false);
|
||||
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
|
||||
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
|
||||
|
||||
const deleteRegex = () => {
|
||||
deleteregex(regexInfo.id).then(res => {
|
||||
@@ -30,6 +32,19 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
}).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}>
|
||||
@@ -38,14 +53,22 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
<Grid.Col span={8}>
|
||||
<Text className={style.regex_text}> {regex_expr}</Text>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<Tooltip label="Delete regex" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="red" opened={tooltipOpened} tooltipId="tooltip-id">
|
||||
<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"
|
||||
aria-describedby="tooltip-id"
|
||||
onFocus={() => setTooltipOpened(false)} onBlur={() => setTooltipOpened(false)}
|
||||
onMouseEnter={() => setTooltipOpened(true)} onMouseLeave={() => setTooltipOpened(false)}
|
||||
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}>
|
||||
@@ -58,9 +81,12 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
/>
|
||||
<Space h="md" />
|
||||
<div className='center-flex'>
|
||||
<Badge size="md" color="green" variant="filled">Service: {regexInfo.service_id}</Badge>
|
||||
<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}>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { ActionIcon, Badge, Grid, MediaQuery, Space, Title, Tooltip } from '@mantine/core';
|
||||
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 { errorNotify, fireUpdateRequest, okNotify, pauseservice, startservice, stopservice } from '../../js/utils';
|
||||
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'
|
||||
|
||||
//"status":"stop"/"wait"/"active"/"pause",
|
||||
function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) {
|
||||
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
||||
|
||||
let status_color = "gray";
|
||||
switch(service.status){
|
||||
@@ -21,6 +24,8 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
||||
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 stopService = async () => {
|
||||
setButtonLoading(true)
|
||||
@@ -68,6 +73,31 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
||||
|
||||
}
|
||||
|
||||
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}>
|
||||
@@ -110,7 +140,21 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
||||
<><Space w="xl" /><Space w="xl" /></>
|
||||
</MediaQuery>
|
||||
<div className="center-flex">
|
||||
{additional_buttons}
|
||||
<Menu>
|
||||
<Menu.Label><b>Rename service</b></Menu.Label>
|
||||
<Menu.Item icon={<BiRename size={18} />} onClick={()=>{}}>Change service name</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Public proxy port</b></Menu.Label>
|
||||
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>{}}>Change port</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Internal proxy port</b></Menu.Label>
|
||||
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen port</Menu.Item>
|
||||
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>{}}>Choose port</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Delete service</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">
|
||||
@@ -159,6 +203,20 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
||||
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}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ export type RegexFilter = {
|
||||
is_blacklist:boolean,
|
||||
is_case_sensitive:boolean,
|
||||
mode:string //C S B => C->S S->C BOTH
|
||||
n_packets:number
|
||||
n_packets:number,
|
||||
active:boolean
|
||||
}
|
||||
|
||||
export type RegexAddForm = {
|
||||
|
||||
@@ -99,6 +99,16 @@ export async function deleteregex(regex_id:number){
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function activateregex(regex_id:number){
|
||||
const { status } = await getapi(`regex/${regex_id}/enable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function deactivateregex(regex_id:number){
|
||||
const { status } = await getapi(`regex/${regex_id}/disable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function startservice(service_id:string){
|
||||
const { status } = await getapi(`service/${service_id}/start`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
@@ -127,6 +137,7 @@ export async function deleteservice(service_id:string) {
|
||||
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
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { ActionIcon, Grid, LoadingOverlay, Space, Title, Tooltip } 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 AddNewRegex from '../components/AddNewRegex';
|
||||
import { BsPlusLg } from "react-icons/bs";
|
||||
import YesNoModal from '../components/YesNoModal';
|
||||
import { RegexFilter, Service } from '../js/models';
|
||||
import { deleteservice, errorNotify, eventUpdateName, fireUpdateRequest, okNotify, regenport, serviceinfo, serviceregexlist } from '../js/utils';
|
||||
import { BsArrowRepeat } from "react-icons/bs"
|
||||
import { errorNotify, eventUpdateName, fireUpdateRequest, serviceinfo, serviceregexlist } from '../js/utils';
|
||||
import { useWindowEvent } from '@mantine/hooks';
|
||||
|
||||
function ServiceDetails() {
|
||||
@@ -54,58 +51,12 @@ function ServiceDetails() {
|
||||
|
||||
const navigator = useNavigate()
|
||||
|
||||
const [deleteModal, setDeleteModal] = useState(false)
|
||||
const [changePortModal, setChangePortModal] = useState(false)
|
||||
const [tooltipDeleteOpened, setTooltipDeleteOpened] = useState(false);
|
||||
const [tooltipChangeOpened, setTooltipChangeOpened] = useState(false);
|
||||
|
||||
const [tooltipAddRegexOpened, setTooltipAddRegexOpened] = useState(false);
|
||||
|
||||
const deleteService = () => {
|
||||
deleteservice(serviceInfo.id).then(res => {
|
||||
if (!res){
|
||||
okNotify("Service delete complete!",`The service ${serviceInfo.id} has been deleted!`)
|
||||
updateInfo();
|
||||
}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(serviceInfo.id).then(res => {
|
||||
if (!res){
|
||||
okNotify("Service port regeneration completed!",`The service ${serviceInfo.id} has changed the internal port!`)
|
||||
updateInfo();
|
||||
}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 <div>
|
||||
<LoadingOverlay visible={loader} />
|
||||
<ServiceRow service={serviceInfo} additional_buttons={<>
|
||||
<Tooltip label="Delete service" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="red" opened={tooltipDeleteOpened} tooltipId="tooltip-delete-id">
|
||||
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"
|
||||
aria-describedby="tooltip-delete-id"
|
||||
onFocus={() => setTooltipDeleteOpened(false)} onBlur={() => setTooltipDeleteOpened(false)}
|
||||
onMouseEnter={() => setTooltipDeleteOpened(true)} onMouseLeave={() => setTooltipDeleteOpened(false)}
|
||||
><BsTrashFill size={22} /></ActionIcon>
|
||||
</Tooltip>
|
||||
<Space w="md"/>
|
||||
<Tooltip label="Change proxy port" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="blue" opened={tooltipChangeOpened} tooltipId="tooltip-change-id">
|
||||
<ActionIcon color="blue" onClick={()=>setChangePortModal(true)} size="xl" radius="md" variant="filled"
|
||||
aria-describedby="tooltip-change-id"
|
||||
onFocus={() => setTooltipChangeOpened(false)} onBlur={() => setTooltipChangeOpened(false)}
|
||||
onMouseEnter={() => setTooltipChangeOpened(true)} onMouseLeave={() => setTooltipChangeOpened(false)}
|
||||
><BsArrowRepeat size={28} /></ActionIcon>
|
||||
</Tooltip>
|
||||
<Space w="md"/>
|
||||
</>}></ServiceRow>
|
||||
|
||||
<ServiceRow service={serviceInfo} />
|
||||
{regexesList.length === 0?<>
|
||||
<Space h="xl" />
|
||||
<Title className='center-flex' align='center' order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
|
||||
@@ -126,20 +77,7 @@ function ServiceDetails() {
|
||||
|
||||
{srv_id?<AddNewRegex opened={open} onClose={closeModal} service={srv_id} />:null}
|
||||
|
||||
<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={deleteService}
|
||||
opened={deleteModal}
|
||||
/>
|
||||
<YesNoModal
|
||||
title='Are you sure to change the proxy internal port?'
|
||||
description={`You are going to change the proxy port '${serviceInfo.internal_port}'. This will cause the shutdown of your service temporarily! ⚠️`}
|
||||
onClose={()=>setChangePortModal(false)}
|
||||
action={changePort}
|
||||
opened={changePortModal}
|
||||
/>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user