fixed mantine forms and menu + improvements on dev mode with vite

This commit is contained in:
Domingo Dirutigliano
2023-06-14 21:49:15 +02:00
parent 2e65d803a2
commit 3f2a1db324
18 changed files with 82 additions and 58 deletions

View File

@@ -16,7 +16,7 @@ async def frontend_debug_proxy(path):
httpc = httpx.AsyncClient()
req = httpc.build_request("GET",f"http://127.0.0.1:{os.getenv('F_PORT','5173')}/"+path)
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, headers=resp.headers)
async def react_deploy(path):
file_request = os.path.join(REACT_BUILD_DIR, path)
@@ -35,10 +35,10 @@ def frontend_deploy(app):
while True:
data = await ws_b.recv()
await ws_a.send_text(data)
@app.websocket("/ws")
@app.websocket("/")
async def websocket_debug_proxy(ws: WebSocket):
await ws.accept()
async with websockets.connect(f"ws://127.0.0.1:{os.getenv('F_PORT','5173')}/ws") as ws_b_client:
async with websockets.connect(f"ws://127.0.0.1:{os.getenv('F_PORT','5173')}/") 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)

View File

@@ -60,7 +60,7 @@ function App() {
password:"",
},
validate:{
password: (value) => value !== ""
password: (value) => value !== "" ? null : "Password is required",
}
})

View File

@@ -25,9 +25,9 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
deactive:false
},
validate:{
regex: (value) => value !== "",
type: (value) => ["blacklist","whitelist"].includes(value),
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
regex: (value) => value !== "" ? null : "Regex is required",
type: (value) => ["blacklist","whitelist"].includes(value) ? null : "Invalid type",
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value) ? null : "Invalid mode",
}
})

View File

@@ -12,7 +12,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () =
expire:true
},
validate:{
password: (value) => value !== ""
password: (value) => value !== ""? null : "Password is required"
}
})
const [loadingBtn, setLoadingBtn] = useState(false)

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { ActionIcon, Divider, Image, Menu, Tooltip, MediaQuery, Burger, Space, Header } from '@mantine/core';
import { ActionIcon, Divider, Image, Menu, Tooltip, MediaQuery, Burger, Space, Header, Button, ThemeIcon } from '@mantine/core';
import style from "./index.module.scss";
import { errorNotify, getmainpath, logout } from '../../js/utils';
import { AiFillHome } from "react-icons/ai"
@@ -9,6 +9,8 @@ import { MdOutlineSettingsBackupRestore } from 'react-icons/md';
import { ImExit } from 'react-icons/im';
import ResetPasswordModal from './ResetPasswordModal';
import ResetModal from './ResetModal';
import { RiMenu5Fill } from 'react-icons/ri';
import { MenuDropDownWithButton } from '../MainLayout';
function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch<React.SetStateAction<boolean>>}) {
@@ -51,15 +53,13 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
<div className="flex-spacer" />
<Menu>
<MenuDropDownWithButton>
<Menu.Label>Firewall Access</Menu.Label>
<Menu.Item icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
<Divider />
<Menu.Label>Actions</Menu.Label>
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
</Menu>
</MenuDropDownWithButton>
<Space w="md" />
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
<ActionIcon color="teal" style={{marginRight:"10px"}}

View File

@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
import { Container, Space } from '@mantine/core';
import { ActionIcon, Container, Menu, Space, ThemeIcon } from '@mantine/core';
import { AppShell } from '@mantine/core';
import NavBar from './NavBar';
import FooterPage from './Footer';
import HeaderPage from './Header';
import { getmainpath } from '../js/utils';
import { useLocation } from 'react-router-dom';
import { RiMenu5Fill } from 'react-icons/ri';
@@ -41,3 +42,14 @@ function MainLayout({ children }:{ children:any }) {
}
export default MainLayout;
export const MenuDropDownWithButton = ({children}:{children:any}) => <Menu withArrow>
<Menu.Target>
<ActionIcon variant='transparent'>
<RiMenu5Fill size={24} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
{children}
</Menu.Dropdown>
</Menu>

View File

@@ -25,10 +25,10 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
autostart: true
},
validate:{
name: (value) => value !== ""?true:false,
port: (value) => value>0 && value<65536,
proto: (value) => ["tcp","udp"].includes(value),
ip_int: (value) => value.match(regex_ipv6)?true:false || value.match(regex_ipv4)?true:false
name: (value) => value !== "" ? null : "Service name is required",
port: (value) => (value>0 && value<65536) ? null : "Invalid port",
proto: (value) => ["tcp","udp"].includes(value) ? null : "Invalid protocol",
ip_int: (value) => (value.match(regex_ipv6) || value.match(regex_ipv4)) ? null : "Invalid IP address",
}
})

View File

@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
const form = useForm({
initialValues: { name:service.name },
validate:{ name: (value) => value !== "" }
validate:{ name: (value) => value !== ""? null : "Service name is required" }
})
const close = () =>{

View File

@@ -9,6 +9,7 @@ import { errorNotify, okNotify, regex_ipv4 } from '../../../js/utils';
import { BsTrashFill } from 'react-icons/bs';
import { BiRename } from 'react-icons/bi'
import RenameForm from './RenameForm';
import { MenuDropDownWithButton } from '../../MainLayout';
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
@@ -108,13 +109,13 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<><Space w="xl" /><Space w="xl" /></>
</MediaQuery>
<div className="center-flex">
<Menu>
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</Menu>
</MenuDropDownWithButton>
<Space w="md"/>
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
<ActionIcon color="red" loading={buttonLoading}

View File

@@ -1,16 +1,16 @@
import { Autocomplete, AutocompleteItem, Space, Title } from "@mantine/core"
import { Autocomplete, AutocompleteItem, Select, Space, Title } from "@mantine/core"
import React, { useEffect, useState } from "react"
import { getipinterfaces } from "../js/utils";
import PortInput from "./PortInput";
import { UseFormReturnType } from "@mantine/form/lib/types";
interface ItemProps extends AutocompleteItem {
label: string;
netint: string;
}
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
({ label, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
( <b>{label}</b> ) -{">"} <b>{value}</b>
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
( <b>{netint}</b> ) -{">"} <b>{value}</b>
</div>
);
@@ -21,7 +21,7 @@ export default function PortAndInterface({ form, int_name, port_name, label }:{
useEffect(()=>{
getipinterfaces().then(data => {
setIpInterfaces(data.map(item => ({label:item.name, value:item.addr})));
setIpInterfaces(data.map(item => ({netint:item.name, value:item.addr, label:item.addr})));
})
},[])
@@ -29,17 +29,26 @@ export default function PortAndInterface({ form, int_name, port_name, label }:{
{label?<>
<Title order={6}>{label}</Title>
<Space h="xs" /></> :null}
<div className='center-flex' style={{width:"100%"}}>
<Autocomplete
placeholder="10.1.1.1"
itemComponent={AutoCompleteItem}
data={ipInterfaces}
{...form.getInputProps(int_name)}
style={{width:"100%"}}
/>
<Space w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" />
<PortInput {...form.getInputProps(port_name)} />
</div>
<div className='center-flex' style={{width:"100%"}}>
<Select
placeholder="10.1.1.1"
itemComponent={AutoCompleteItem}
data={ipInterfaces}
searchable
dropdownPosition="bottom"
maxDropdownHeight={100}
creatable
getCreateLabel={(query) => `+ Use this: ${query}`}
onCreate={(query) => {
const item = { value: query, netint: "CUSTOM", label: query };
setIpInterfaces((current) => [...current, item]);
return item;
}}
{...form.getInputProps(int_name)}
style={{width:"100%"}}
/>
<Space w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" />
<PortInput {...form.getInputProps(port_name)} />
</div>
</>
}

View File

@@ -29,12 +29,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
autostart: false,
},
validate:{
name: (value) => value !== ""?true:false,
public_port: (value) => value>0 && value<65536,
proxy_port: (value) => value>0 && value<65536,
proto: (value) => ["tcp","udp"].includes(value),
ip_src: (value) => value.match(regex_ipv6_no_cidr)?true:false || value.match(regex_ipv4_no_cidr)?true:false,
ip_dst: (value) => value.match(regex_ipv6_no_cidr)?true:false || value.match(regex_ipv4_no_cidr)?true:false
name: (value) => value !== ""? null : "Service name is required",
public_port: (value) => (value>0 && value<65536) ? null : "Invalid public port",
proxy_port: (value) => (value>0 && value<65536) ? null : "Invalid proxy port",
proto: (value) => ["tcp","udp"].includes(value) ? null : "Invalid protocol",
ip_src: (value) => (value.match(regex_ipv6_no_cidr) || value.match(regex_ipv4_no_cidr)) ? null : "Invalid source IP address",
ip_dst: (value) => (value.match(regex_ipv6_no_cidr) || value.match(regex_ipv4_no_cidr)) ? null : "Invalid destination IP address",
}
})

View File

@@ -14,8 +14,8 @@ function ChangeDestination({ opened, onClose, service }:{ opened:boolean, onClos
proxy_port:service.proxy_port
},
validate:{
proxy_port: (value) => value>0 && value<65536,
ip_dst: (value) => value.match(regex_ipv6_no_cidr)?true:false || value.match(regex_ipv4_no_cidr)?true:false
proxy_port: (value) => (value>0 && value<65536) ? null : "Invalid proxy port",
ip_dst: (value) => (value.match(regex_ipv6_no_cidr) || value.match(regex_ipv4_no_cidr))? null : "Invalid destination IP address",
}
})

View File

@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
const form = useForm({
initialValues: { name:service.name },
validate:{ name: (value) => value !== "" }
validate:{ name: (value) => value !== ""? null : "Service name is required" }
})
const close = () =>{

View File

@@ -11,6 +11,7 @@ import RenameForm from './RenameForm';
import ChangeDestination from './ChangeDestination';
import PortInput from '../../PortInput';
import { useForm } from '@mantine/form';
import { MenuDropDownWithButton } from '../../MainLayout';
function ServiceRow({ service }:{ service:Service }) {
@@ -25,7 +26,7 @@ function ServiceRow({ service }:{ service:Service }) {
const form = useForm({
initialValues: { proxy_port:service.proxy_port },
validate:{ proxy_port: (value) => value > 0 && value < 65536 }
validate:{ proxy_port: (value) => (value > 0 && value < 65536)? null : "Invalid proxy port" }
})
const onChangeProxyPort = ({proxy_port}:{proxy_port:number}) => {
@@ -132,7 +133,7 @@ function ServiceRow({ service }:{ service:Service }) {
<Space w="xl" /><Space w="xl" />
<div className="center-flex">
<Menu>
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Menu.Label><b>Change destination</b></Menu.Label>
@@ -140,7 +141,7 @@ function ServiceRow({ service }:{ service:Service }) {
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</Menu>
</MenuDropDownWithButton>
<Space w="md"/>
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
<ActionIcon color="red" loading={buttonLoading}

View File

@@ -25,9 +25,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
autostart: true
},
validate:{
name: (value) => value !== ""?true:false,
port: (value) => value>0 && value<65536,
internalPort: (value) => value>0 && value<65536,
name: (value) => value !== ""? null : "Service name is required",
port: (value) => (value>0 && value<65536) ? null : "Invalid port",
internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port",
}
})

View File

@@ -20,8 +20,8 @@ function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:
port: service.public_port
},
validate:{
internalPort: (value) => value>0 && value<65536,
port: (value) => value>0 && value<65536
internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port",
port: (value) => (value>0 && value<65536) ? null : "Invalid public port",
}
})

View File

@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
const form = useForm({
initialValues: { name:service.name },
validate:{ name: (value) => value !== "" }
validate:{ name: (value) => value !== ""? null : "Service name is required" }
})
const close = () =>{

View File

@@ -11,6 +11,7 @@ import { BiRename } from 'react-icons/bi'
import ChangePortModal from './ChangePortModal';
import RenameForm from './RenameForm';
import { regexproxy, Service } from '../utils';
import { MenuDropDownWithButton } from '../../MainLayout';
//"status":"stop"/"wait"/"active"/"pause",
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
@@ -139,7 +140,7 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<><Space w="xl" /><Space w="xl" /></>
</MediaQuery>
<div className="center-flex">
<Menu>
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Divider />
@@ -149,7 +150,7 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</Menu>
</MenuDropDownWithButton>
<Space w="md"/>
{["pause","wait"].includes(service.status)?