Improve stability and functionalities
This commit is contained in:
@@ -36,8 +36,9 @@ def JWT_SECRET(): return conf.get("secret")
|
|||||||
|
|
||||||
@app.on_event("shutdown")
|
@app.on_event("shutdown")
|
||||||
async def shutdown_event():
|
async def shutdown_event():
|
||||||
await firewall.close()
|
|
||||||
db.disconnect()
|
db.disconnect()
|
||||||
|
await firewall.close()
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def startup_event():
|
async def startup_event():
|
||||||
@@ -197,7 +198,7 @@ async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)
|
|||||||
return db.query("""
|
return db.query("""
|
||||||
SELECT
|
SELECT
|
||||||
regex, mode, regex_id `id`, service_id, is_blacklist,
|
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 = ?;
|
FROM regexes WHERE service_id = ?;
|
||||||
""", service_id)
|
""", service_id)
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)):
|
|||||||
res = db.query("""
|
res = db.query("""
|
||||||
SELECT
|
SELECT
|
||||||
regex, mode, regex_id `id`, service_id, is_blacklist,
|
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` = ?;
|
FROM regexes WHERE `id` = ?;
|
||||||
""", regex_id)
|
""", regex_id)
|
||||||
if len(res) == 0: raise HTTPException(status_code=400, detail="This regex does not exists!")
|
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'}
|
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):
|
class RegexAddForm(BaseModel):
|
||||||
service_id: str
|
service_id: str
|
||||||
regex: 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):
|
async def frontend_debug_proxy(path):
|
||||||
httpc = httpx.AsyncClient()
|
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)
|
resp = await httpc.send(req, stream=True)
|
||||||
return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code)
|
return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code)
|
||||||
|
|
||||||
@@ -287,7 +304,7 @@ if DEBUG:
|
|||||||
@app.websocket("/ws")
|
@app.websocket("/ws")
|
||||||
async def websocket_debug_proxy(ws: WebSocket):
|
async def websocket_debug_proxy(ws: WebSocket):
|
||||||
await ws.accept()
|
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))
|
fwd_task = asyncio.create_task(forward_websocket(ws, ws_b_client))
|
||||||
rev_task = asyncio.create_task(reverse_websocket(ws, ws_b_client))
|
rev_task = asyncio.create_task(reverse_websocket(ws, ws_b_client))
|
||||||
await asyncio.gather(fwd_task, rev_task)
|
await asyncio.gather(fwd_task, rev_task)
|
||||||
@@ -298,7 +315,7 @@ async def catch_all(full_path:str):
|
|||||||
try:
|
try:
|
||||||
return await frontend_debug_proxy(full_path)
|
return await frontend_debug_proxy(full_path)
|
||||||
except Exception:
|
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)
|
else: return await react_deploy(full_path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class Proxy:
|
|||||||
if not self.isactive():
|
if not self.isactive():
|
||||||
try:
|
try:
|
||||||
self.filter_map = self.compile_filters()
|
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")
|
proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"./proxy")
|
||||||
|
|
||||||
self.process = await asyncio.create_subprocess_exec(
|
self.process = await asyncio.create_subprocess_exec(
|
||||||
@@ -57,8 +57,9 @@ class Proxy:
|
|||||||
if stdout_line.startswith("BLOCKED"):
|
if stdout_line.startswith("BLOCKED"):
|
||||||
regex_id = stdout_line.split()[1]
|
regex_id = stdout_line.split()[1]
|
||||||
async with self.filter_map_lock:
|
async with self.filter_map_lock:
|
||||||
self.filter_map[regex_id].blocked+=1
|
if regex_id in self.filter_map:
|
||||||
if self.callback_blocked_update: self.callback_blocked_update(self.filter_map[regex_id])
|
self.filter_map[regex_id].blocked+=1
|
||||||
|
if self.callback_blocked_update: self.callback_blocked_update(self.filter_map[regex_id])
|
||||||
except Exception:
|
except Exception:
|
||||||
return await self.process.wait()
|
return await self.process.wait()
|
||||||
else:
|
else:
|
||||||
@@ -87,9 +88,14 @@ class Proxy:
|
|||||||
if self.isactive():
|
if self.isactive():
|
||||||
async with self.filter_map_lock:
|
async with self.filter_map_lock:
|
||||||
self.filter_map = self.compile_filters()
|
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)
|
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):
|
def isactive(self):
|
||||||
return self.process and self.process.returncode is None
|
return self.process and self.process.returncode is None
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class SQLite():
|
|||||||
'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0',
|
'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0',
|
||||||
'regex_id': 'INTEGER PRIMARY KEY',
|
'regex_id': 'INTEGER PRIMARY KEY',
|
||||||
'is_case_sensitive' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1))',
|
'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)',
|
'FOREIGN KEY (service_id)':'REFERENCES services (service_id)',
|
||||||
},
|
},
|
||||||
'keys_values': {
|
'keys_values': {
|
||||||
@@ -109,6 +110,7 @@ class ServiceManager:
|
|||||||
callback_blocked_update=self._stats_updater
|
callback_blocked_update=self._stats_updater
|
||||||
)
|
)
|
||||||
self.status = STATUS.STOP
|
self.status = STATUS.STOP
|
||||||
|
self.wanted_status = STATUS.STOP
|
||||||
self.filters = {}
|
self.filters = {}
|
||||||
self._update_port_from_db()
|
self._update_port_from_db()
|
||||||
self._update_filters_from_db()
|
self._update_filters_from_db()
|
||||||
@@ -131,7 +133,7 @@ class ServiceManager:
|
|||||||
SELECT
|
SELECT
|
||||||
regex, mode, regex_id `id`, is_blacklist,
|
regex, mode, regex_id `id`, is_blacklist,
|
||||||
blocked_packets n_packets, is_case_sensitive
|
blocked_packets n_packets, is_case_sensitive
|
||||||
FROM regexes WHERE service_id = ?;
|
FROM regexes WHERE service_id = ? AND active=1;
|
||||||
""", self.id)
|
""", self.id)
|
||||||
|
|
||||||
#Filter check
|
#Filter check
|
||||||
@@ -162,26 +164,30 @@ class ServiceManager:
|
|||||||
|
|
||||||
async def next(self,to):
|
async def next(self,to):
|
||||||
async with self.lock:
|
async with self.lock:
|
||||||
if self.status != to:
|
return await self._next(to)
|
||||||
# ACTIVE -> PAUSE or PAUSE -> ACTIVE
|
|
||||||
if (self.status, to) in [(STATUS.ACTIVE, STATUS.PAUSE)]:
|
|
||||||
await self.proxy.pause()
|
|
||||||
self._set_status(to)
|
|
||||||
|
|
||||||
elif (self.status, to) in [(STATUS.PAUSE, STATUS.ACTIVE)]:
|
async def _next(self, to):
|
||||||
await self.proxy.reload()
|
if self.status != to:
|
||||||
self._set_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.PAUSE, STATUS.ACTIVE)]:
|
||||||
elif (self.status,to) in [(STATUS.ACTIVE, STATUS.STOP), (STATUS.WAIT, STATUS.STOP), (STATUS.PAUSE, STATUS.STOP)]: #Stop proxy
|
await self.proxy.reload()
|
||||||
if self.starter: self.starter.cancel()
|
self._set_status(to)
|
||||||
await self.proxy.stop()
|
|
||||||
self._set_status(to)
|
|
||||||
|
|
||||||
# STOP -> ACTIVE or STOP -> PAUSE
|
# ACTIVE -> STOP
|
||||||
elif (self.status, to) in [(STATUS.STOP, STATUS.ACTIVE), (STATUS.STOP, STATUS.PAUSE)]:
|
elif (self.status,to) in [(STATUS.ACTIVE, STATUS.STOP), (STATUS.WAIT, STATUS.STOP), (STATUS.PAUSE, STATUS.STOP)]: #Stop proxy
|
||||||
self._set_status(STATUS.WAIT)
|
if self.starter: self.starter.cancel()
|
||||||
self.__proxy_starter(to)
|
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):
|
def _stats_updater(self,filter:Filter):
|
||||||
@@ -191,7 +197,9 @@ class ServiceManager:
|
|||||||
async with self.lock:
|
async with self.lock:
|
||||||
self._update_port_from_db()
|
self._update_port_from_db()
|
||||||
if self.status in [STATUS.PAUSE, STATUS.ACTIVE]:
|
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):
|
def _set_status(self,status):
|
||||||
self.status = status
|
self.status = status
|
||||||
@@ -233,7 +241,7 @@ class ProxyManager:
|
|||||||
async def remove(self,id):
|
async def remove(self,id):
|
||||||
async with self.lock:
|
async with self.lock:
|
||||||
if id in self.proxy_table:
|
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]
|
del self.proxy_table[id]
|
||||||
|
|
||||||
async def reload(self):
|
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": {
|
"files": {
|
||||||
"main.css": "/static/css/main.c375ae17.css",
|
"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",
|
"index.html": "/index.html",
|
||||||
"main.c375ae17.css.map": "/static/css/main.c375ae17.css.map",
|
"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": [
|
"entrypoints": [
|
||||||
"static/css/main.c375ae17.css",
|
"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({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name:"",
|
name:"",
|
||||||
port:1,
|
port:8080,
|
||||||
|
internalPort:30001,
|
||||||
|
chosenInternalPort:false,
|
||||||
autostart: true
|
autostart: true
|
||||||
},
|
},
|
||||||
validationRules:{
|
validationRules:{
|
||||||
name: (value) => value !== ""?true:false,
|
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"
|
placeholder="8080"
|
||||||
min={1}
|
min={1}
|
||||||
max={65535}
|
max={65535}
|
||||||
label="Service port"
|
label="Public Service port"
|
||||||
{...form.getInputProps('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" />
|
<Space h="md" />
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
label="Auto-Start Service"
|
label="Choose internal port"
|
||||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Group position="right" mt="md">
|
<Group position="right" mt="md">
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
|
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { RegexFilter } from '../../js/models';
|
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 style from "./RegexView.module.scss";
|
||||||
import { BsTrashFill } from "react-icons/bs"
|
import { BsTrashFill } from "react-icons/bs"
|
||||||
import YesNoModal from '../YesNoModal';
|
import YesNoModal from '../YesNoModal';
|
||||||
import FilterTypeSelector from '../FilterTypeSelector';
|
import FilterTypeSelector from '../FilterTypeSelector';
|
||||||
|
import { FaPause, FaPlay } from 'react-icons/fa';
|
||||||
|
|
||||||
|
|
||||||
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||||
@@ -17,7 +18,8 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
let regex_expr = getHumanReadableRegex(regexInfo.regex);
|
let regex_expr = getHumanReadableRegex(regexInfo.regex);
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
const [tooltipOpened, setTooltipOpened] = useState(false);
|
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
|
||||||
|
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
|
||||||
|
|
||||||
const deleteRegex = () => {
|
const deleteRegex = () => {
|
||||||
deleteregex(regexInfo.id).then(res => {
|
deleteregex(regexInfo.id).then(res => {
|
||||||
@@ -30,6 +32,19 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
}).catch( err => errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${err}`))
|
}).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}>
|
return <div className={style.box}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
@@ -38,14 +53,22 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
<Grid.Col span={8}>
|
<Grid.Col span={8}>
|
||||||
<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} className='center-flex'>
|
||||||
<Tooltip label="Delete regex" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="red" opened={tooltipOpened} tooltipId="tooltip-id">
|
<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"
|
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"
|
||||||
aria-describedby="tooltip-id"
|
onFocus={() => setDeleteTooltipOpened(false)} onBlur={() => setDeleteTooltipOpened(false)}
|
||||||
onFocus={() => setTooltipOpened(false)} onBlur={() => setTooltipOpened(false)}
|
onMouseEnter={() => setDeleteTooltipOpened(true)} onMouseLeave={() => setDeleteTooltipOpened(false)}
|
||||||
onMouseEnter={() => setTooltipOpened(true)} onMouseLeave={() => setTooltipOpened(false)}
|
|
||||||
><BsTrashFill size={22} /></ActionIcon>
|
><BsTrashFill size={22} /></ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
</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}>
|
||||||
@@ -58,9 +81,12 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
/>
|
/>
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
<div className='center-flex'>
|
<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" />
|
<Space w="xs" />
|
||||||
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
|
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col style={{width:"100%"}} span={6}>
|
<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 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';
|
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",
|
//"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";
|
let status_color = "gray";
|
||||||
switch(service.status){
|
switch(service.status){
|
||||||
@@ -21,6 +24,8 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
|||||||
const [stopModal, setStopModal] = useState(false);
|
const [stopModal, setStopModal] = useState(false);
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
||||||
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
|
const [changePortModal, setChangePortModal] = useState(false)
|
||||||
|
|
||||||
const stopService = async () => {
|
const stopService = async () => {
|
||||||
setButtonLoading(true)
|
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 <>
|
return <>
|
||||||
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
|
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
|
||||||
<Grid.Col md={4} xs={12}>
|
<Grid.Col md={4} xs={12}>
|
||||||
@@ -110,7 +140,21 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
|
|||||||
<><Space w="xl" /><Space w="xl" /></>
|
<><Space w="xl" /><Space w="xl" /></>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<div className="center-flex">
|
<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)?
|
{["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">
|
<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}
|
opened={stopModal}
|
||||||
/>
|
/>
|
||||||
<hr style={{width:"100%"}}/>
|
<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_blacklist:boolean,
|
||||||
is_case_sensitive:boolean,
|
is_case_sensitive: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,
|
||||||
|
active:boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RegexAddForm = {
|
export type RegexAddForm = {
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ export async function deleteregex(regex_id:number){
|
|||||||
return status === "ok"?undefined:status
|
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){
|
export async function startservice(service_id:string){
|
||||||
const { status } = await getapi(`service/${service_id}/start`) as ServerResponse;
|
const { status } = await getapi(`service/${service_id}/start`) as ServerResponse;
|
||||||
return status === "ok"?undefined:status
|
return status === "ok"?undefined:status
|
||||||
@@ -127,6 +137,7 @@ export async function deleteservice(service_id:string) {
|
|||||||
return status === "ok"?undefined:status
|
return status === "ok"?undefined:status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function addregex(data:RegexAddForm) {
|
export async function addregex(data:RegexAddForm) {
|
||||||
const { status } = await postapi("regexes/add",data) as ServerResponse;
|
const { status } = await postapi("regexes/add",data) as ServerResponse;
|
||||||
return status === "ok"?undefined:status
|
return status === "ok"?undefined:status
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import { ActionIcon, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
|
import { ActionIcon, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { BsTrashFill } from 'react-icons/bs';
|
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, 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 AddNewRegex from '../components/AddNewRegex';
|
import AddNewRegex from '../components/AddNewRegex';
|
||||||
import { BsPlusLg } from "react-icons/bs";
|
import { BsPlusLg } from "react-icons/bs";
|
||||||
import YesNoModal from '../components/YesNoModal';
|
|
||||||
import { RegexFilter, Service } from '../js/models';
|
import { RegexFilter, Service } from '../js/models';
|
||||||
import { deleteservice, errorNotify, eventUpdateName, fireUpdateRequest, okNotify, regenport, serviceinfo, serviceregexlist } from '../js/utils';
|
import { errorNotify, eventUpdateName, fireUpdateRequest, serviceinfo, serviceregexlist } from '../js/utils';
|
||||||
import { BsArrowRepeat } from "react-icons/bs"
|
|
||||||
import { useWindowEvent } from '@mantine/hooks';
|
import { useWindowEvent } from '@mantine/hooks';
|
||||||
|
|
||||||
function ServiceDetails() {
|
function ServiceDetails() {
|
||||||
@@ -54,58 +51,12 @@ function ServiceDetails() {
|
|||||||
|
|
||||||
const navigator = useNavigate()
|
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 [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>
|
return <div>
|
||||||
<LoadingOverlay visible={loader} />
|
<LoadingOverlay visible={loader} />
|
||||||
<ServiceRow service={serviceInfo} additional_buttons={<>
|
<ServiceRow service={serviceInfo} />
|
||||||
<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>
|
|
||||||
|
|
||||||
{regexesList.length === 0?<>
|
{regexesList.length === 0?<>
|
||||||
<Space h="xl" />
|
<Space h="xl" />
|
||||||
<Title className='center-flex' align='center' order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
|
<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}
|
{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>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user