diff --git a/backend/routers/nfregex.py b/backend/routers/nfregex.py
index ef86980..45856d1 100644
--- a/backend/routers/nfregex.py
+++ b/backend/routers/nfregex.py
@@ -42,7 +42,7 @@ class RegexAddForm(BaseModel):
service_id: str
regex: str
mode: str
- active: bool|None
+ active: bool|None = None
is_blacklist: bool
is_case_sensitive: bool
@@ -54,7 +54,7 @@ class ServiceAddForm(BaseModel):
class ServiceAddResponse(BaseModel):
status:str
- service_id: str|None
+ service_id: str|None = None
app = APIRouter()
diff --git a/backend/routers/porthijack.py b/backend/routers/porthijack.py
index 54c1314..18d1bd1 100644
--- a/backend/routers/porthijack.py
+++ b/backend/routers/porthijack.py
@@ -32,7 +32,7 @@ class ServiceAddForm(BaseModel):
class ServiceAddResponse(BaseModel):
status:str
- service_id: str|None
+ service_id: str|None = None
class GeneralStatModel(BaseModel):
services: int
diff --git a/backend/routers/regexproxy.py b/backend/routers/regexproxy.py
index eec4834..ca60dc0 100644
--- a/backend/routers/regexproxy.py
+++ b/backend/routers/regexproxy.py
@@ -156,8 +156,8 @@ async def regen_service_port(service_id: str):
return {'status': 'ok'}
class ChangePortForm(BaseModel):
- port: int|None
- internalPort: int|None
+ port: int|None = None
+ internalPort: int|None = None
@app.post('/service/{service_id}/change-ports', response_model=StatusMessageModel)
async def change_service_ports(service_id: str, change_port:ChangePortForm):
@@ -249,7 +249,7 @@ class RegexAddForm(BaseModel):
service_id: str
regex: str
mode: str
- active: bool|None
+ active: bool|None = None
is_blacklist: bool
is_case_sensitive: bool
@@ -272,11 +272,11 @@ async def add_new_regex(form: RegexAddForm):
class ServiceAddForm(BaseModel):
name: str
port: PortType
- internalPort: int|None
+ internalPort: int|None = None
class ServiceAddStatus(BaseModel):
status:str
- id: str|None
+ id: str|None = None
class RenameForm(BaseModel):
name:str
diff --git a/backend/utils/loader.py b/backend/utils/loader.py
index c37327a..3db07d0 100644
--- a/backend/utils/loader.py
+++ b/backend/utils/loader.py
@@ -48,10 +48,10 @@ def list_routers():
return [ele[:-3] for ele in list_files(ROUTERS_DIR) if ele != "__init__.py" and " " not in ele and ele.endswith(".py")]
class RouterModule():
- router: None|APIRouter
- reset: None|Callable
- startup: None|Callable
- shutdown: None|Callable
+ router: APIRouter|None = None
+ reset: Callable|None = None
+ startup: Callable|None = None
+ shutdown: Callable|None = None
name: str
def __init__(self, router: APIRouter, reset: Callable, startup: Callable, shutdown: Callable, name:str):
diff --git a/backend/utils/models.py b/backend/utils/models.py
index 46128e7..962c0b1 100644
--- a/backend/utils/models.py
+++ b/backend/utils/models.py
@@ -17,7 +17,7 @@ class PasswordChangeForm(BaseModel):
class ChangePasswordModel(BaseModel):
status: str
- access_token: str|None
+ access_token: str|None = None
class IpInterface(BaseModel):
addr: str
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 70ca16f..a6e4831 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -5,15 +5,17 @@ import { ImCross } from 'react-icons/im';
import { Outlet, Route, Routes } from 'react-router-dom';
import MainLayout from './components/MainLayout';
import { PasswordSend, ServerStatusResponse } from './js/models';
-import { errorNotify, fireUpdateRequest, getstatus, HomeRedirector, IS_DEV, login, setpassword } from './js/utils';
+import { DEV_IP_BACKEND, errorNotify, fireUpdateRequest, getstatus, HomeRedirector, IS_DEV, login, setpassword } from './js/utils';
import NFRegex from './pages/NFRegex';
import io from 'socket.io-client';
import RegexProxy from './pages/RegexProxy';
import ServiceDetailsNFRegex from './pages/NFRegex/ServiceDetails';
import ServiceDetailsProxyRegex from './pages/RegexProxy/ServiceDetails';
import PortHijack from './pages/PortHijack';
+import { Firewall } from './pages/Firewall';
-const socket = io({transports: ["websocket", "polling"], path:"/sock", host:IS_DEV?"127.0.0.1:4444":undefined });
+
+const socket = IS_DEV?io("ws://"+DEV_IP_BACKEND, {transports: ["websocket", "polling"], path:"/sock" }):io({transports: ["websocket", "polling"], path:"/sock"});
function App() {
@@ -154,6 +156,7 @@ function App() {
} >
} />
+ } />
} />
} />
diff --git a/frontend/src/components/Firewall/utils.ts b/frontend/src/components/Firewall/utils.ts
new file mode 100644
index 0000000..3906a64
--- /dev/null
+++ b/frontend/src/components/Firewall/utils.ts
@@ -0,0 +1,70 @@
+import { RegexAddForm, RegexFilter, ServerResponse } from "../../js/models"
+import { getapi, postapi } from "../../js/utils"
+
+export type GeneralStats = {
+ rules:number,
+}
+
+export enum Protocol {
+ TCP = "tcp",
+ UDP = "udp",
+ ANY = "any"
+}
+
+export enum ActionType {
+ ACCEPT = "accept",
+ DROP = "drop",
+ REJECT = "reject"
+}
+
+export enum RuleMode {
+ OUT = "O",
+ IN = "I",
+}
+
+export type Rule = {
+ active: boolean
+ name:string,
+ proto: Protocol,
+ ip_src: string,
+ ip_dst: string,
+ port_src_from: number,
+ port_dst_from: number,
+ port_src_to: number,
+ port_dst_to: number,
+ action: ActionType,
+ mode: RuleMode,
+}
+
+export type RuleInfo = {
+ rules: Rule[]
+ policy: ActionType
+}
+
+
+export type ServerResponseListed = {
+ status:(ServerResponse & {rule_id:number})[]|string,
+}
+
+
+export const firewall = {
+ stats: async () => {
+ return await getapi("firewall/stats") as GeneralStats;
+ },
+ rules: async() => {
+ return await getapi("firewall/rules") as RuleInfo;
+ },
+ rulenable: async (rule_id:number) => {
+ return await getapi(`firewall/rule/${rule_id}/enable`) as ServerResponse;
+ },
+ ruledisable: async (rule_id:number) => {
+ return await getapi(`firewall/rule/${rule_id}/disable`) as ServerResponse;
+ },
+ rulerename: async (rule_id:number, name: string) => {
+ const { status } = await postapi(`firewall/rule/${rule_id}/rename`,{ name }) as ServerResponse;
+ return status === "ok"?undefined:status
+ },
+ servicesadd: async (data:RuleInfo) => {
+ return await postapi("firewall/rules/set", data) as ServerResponseListed;
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/components/NavBar/index.tsx b/frontend/src/components/NavBar/index.tsx
index b6bf486..c340747 100644
--- a/frontend/src/components/NavBar/index.tsx
+++ b/frontend/src/components/NavBar/index.tsx
@@ -1,13 +1,14 @@
-import { Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
-import React from "react";
+import { Box, Collapse, Divider, Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
+import { useState } from "react";
import { IoMdGitNetwork } from "react-icons/io";
-import { MdTransform } from "react-icons/md";
+import { MdOutlineExpandLess, MdOutlineExpandMore, MdTransform } from "react-icons/md";
import { useNavigate } from "react-router-dom";
import { getmainpath } from "../../js/utils";
import { GrDirections } from "react-icons/gr";
+import { PiWallLight } from "react-icons/pi";
-function NavBarButton({ navigate, closeNav, name, icon, color, disabled }:
- { navigate: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean }) {
+function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick }:
+ { navigate?: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean, onClick?:CallableFunction }) {
const navigator = useNavigate()
return ({
@@ -22,7 +23,10 @@ function NavBarButton({ navigate, closeNav, name, icon, color, disabled }:
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
- })} onClick={()=>{navigator(`/${navigate}`);closeNav()}} disabled={disabled}>
+ })} onClick={()=>{
+ if(navigate){navigator(`/${navigate}`);closeNav()}
+ if (onClick) onClick()
+ }} disabled={disabled}>
{icon}
@@ -33,16 +37,26 @@ function NavBarButton({ navigate, closeNav, name, icon, color, disabled }:
}
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) {
+ const [toggleState, setToggleState] = useState(false);
+ const advancedPaths = ["regexproxy"]
+ const advancedSelected = advancedPaths.includes(getmainpath())
+ const toggle = (toggleState||advancedSelected)
return
[Fi]*regex 🔥
-
+
+
- } />
- } />
- } />
+ } />
+ } />
+ } />
+
+ : } onClick={()=>setToggleState(!toggleState)} disabled={advancedSelected}/>
+
+ } />
+
diff --git a/frontend/src/components/PortHijack/utils.ts b/frontend/src/components/PortHijack/utils.ts
index 12cbe4a..71fa552 100644
--- a/frontend/src/components/PortHijack/utils.ts
+++ b/frontend/src/components/PortHijack/utils.ts
@@ -25,10 +25,7 @@ export type ServiceAddForm = {
ip_dst: string,
}
-export type ServiceAddResponse = {
- status: string,
- service_id?: string,
-}
+export type ServiceAddResponse = ServerResponse & { service_id: string }
export const porthijack = {
stats: async () => {
diff --git a/frontend/src/components/PortInput.tsx b/frontend/src/components/PortInput.tsx
index 58d11ef..2440b9f 100644
--- a/frontend/src/components/PortInput.tsx
+++ b/frontend/src/components/PortInput.tsx
@@ -6,15 +6,15 @@ interface PortInputProps extends NumberInputProps {
}
const PortInput = React.forwardRef( (props, ref) => {
-
const [oldValue, setOldValue] = useState(props.defaultValue?props.defaultValue.toString():"")
+ const {fullWidth, ...propeties} = props
return {
const value = parseInt((e.target as HTMLInputElement).value)
if (value > 65535) {
@@ -28,7 +28,7 @@ const PortInput = React.forwardRef( (props, r
props.onInput?.(e)
}}
ref={ref}
- {...props}
+ {...propeties}
/>
})
diff --git a/frontend/src/js/utils.tsx b/frontend/src/js/utils.tsx
index 1805850..47882db 100644
--- a/frontend/src/js/utils.tsx
+++ b/frontend/src/js/utils.tsx
@@ -16,10 +16,12 @@ export const regex_ipv6_no_cidr = "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|
export const regex_ipv4 = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(3[0-2]|[1-2][0-9]|[0-9]))?$"
export const regex_ipv4_no_cidr = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
+export const DEV_IP_BACKEND = "127.0.0.1:4444"
+
export async function getapi(path:string):Promise{
return await new Promise((resolve, reject) => {
- fetch(`${IS_DEV?"http://127.0.0.1:4444":""}/api/${path}`,{
+ fetch(`${IS_DEV?`http://${DEV_IP_BACKEND}`:""}/api/${path}`,{
credentials: "same-origin",
headers: { "Authorization" : "Bearer " + window.localStorage.getItem("access_token")}
}).then(res => {
@@ -35,7 +37,7 @@ export async function getapi(path:string):Promise{
export async function postapi(path:string,data:any,is_form:boolean=false):Promise{
return await new Promise((resolve, reject) => {
- fetch(`${IS_DEV?"http://127.0.0.1:4444":""}/api/${path}`, {
+ fetch(`${IS_DEV?`http://${DEV_IP_BACKEND}`:""}/api/${path}`, {
method: 'POST',
credentials: "same-origin",
cache: 'no-cache',
diff --git a/frontend/src/pages/Firewall/index.tsx b/frontend/src/pages/Firewall/index.tsx
new file mode 100644
index 0000000..0df70da
--- /dev/null
+++ b/frontend/src/pages/Firewall/index.tsx
@@ -0,0 +1,55 @@
+import { ActionIcon, Badge, LoadingOverlay, Space, Title, Tooltip } from "@mantine/core"
+import { useEffect, useState } from "react";
+import { BsPlusLg } from "react-icons/bs"
+import { ActionType, GeneralStats, RuleInfo, firewall } from "../../components/Firewall/utils";
+import { errorNotify, eventUpdateName, fireUpdateRequest } from "../../js/utils";
+import { useWindowEvent } from "@mantine/hooks";
+
+
+
+export const Firewall = () => {
+
+ const [generalStats, setGeneralStats] = useState({ rules: 0 });
+ const [rules, setRules] = useState({rules:[], policy:ActionType.ACCEPT});
+ const [loader, setLoader] = useState(true);
+ const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
+ const [open, setOpen] = useState(false);
+
+
+ const updateInfo = async () => {
+
+ await Promise.all([
+ firewall.stats().then(res => {
+ setGeneralStats(res)
+ }).catch(
+ err => errorNotify("General Info Auto-Update failed!", err.toString())
+ ),
+ firewall.rules().then(res => {
+ setRules(res)
+ }).catch(err => {
+ errorNotify("Home Page Auto-Update failed!", err.toString())
+ })
+ ])
+ setLoader(false)
+ }
+
+ useWindowEvent(eventUpdateName, updateInfo)
+ useEffect(fireUpdateRequest,[])
+
+
+ return <>
+
+
+
Firewall Rules
+
+
Rules: {generalStats.rules}
+
+
+ setOpen(true)} size="lg" radius="md" variant="filled"
+ onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
+ onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}>
+
+
+
+ >
+}
\ No newline at end of file