fixed mantine forms and menu + improvements on dev mode with vite
This commit is contained in:
@@ -16,7 +16,7 @@ async def frontend_debug_proxy(path):
|
|||||||
httpc = httpx.AsyncClient()
|
httpc = httpx.AsyncClient()
|
||||||
req = httpc.build_request("GET",f"http://127.0.0.1:{os.getenv('F_PORT','5173')}/"+path)
|
req = httpc.build_request("GET",f"http://127.0.0.1:{os.getenv('F_PORT','5173')}/"+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, headers=resp.headers)
|
||||||
|
|
||||||
async def react_deploy(path):
|
async def react_deploy(path):
|
||||||
file_request = os.path.join(REACT_BUILD_DIR, path)
|
file_request = os.path.join(REACT_BUILD_DIR, path)
|
||||||
@@ -35,10 +35,10 @@ def frontend_deploy(app):
|
|||||||
while True:
|
while True:
|
||||||
data = await ws_b.recv()
|
data = await ws_b.recv()
|
||||||
await ws_a.send_text(data)
|
await ws_a.send_text(data)
|
||||||
@app.websocket("/ws")
|
@app.websocket("/")
|
||||||
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://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))
|
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)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ function App() {
|
|||||||
password:"",
|
password:"",
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
password: (value) => value !== ""
|
password: (value) => value !== "" ? null : "Password is required",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
|
|||||||
deactive:false
|
deactive:false
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
regex: (value) => value !== "",
|
regex: (value) => value !== "" ? null : "Regex is required",
|
||||||
type: (value) => ["blacklist","whitelist"].includes(value),
|
type: (value) => ["blacklist","whitelist"].includes(value) ? null : "Invalid type",
|
||||||
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
|
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value) ? null : "Invalid mode",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () =
|
|||||||
expire:true
|
expire:true
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
password: (value) => value !== ""
|
password: (value) => value !== ""? null : "Password is required"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const [loadingBtn, setLoadingBtn] = useState(false)
|
const [loadingBtn, setLoadingBtn] = useState(false)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
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 style from "./index.module.scss";
|
||||||
import { errorNotify, getmainpath, logout } from '../../js/utils';
|
import { errorNotify, getmainpath, logout } from '../../js/utils';
|
||||||
import { AiFillHome } from "react-icons/ai"
|
import { AiFillHome } from "react-icons/ai"
|
||||||
@@ -9,6 +9,8 @@ import { MdOutlineSettingsBackupRestore } from 'react-icons/md';
|
|||||||
import { ImExit } from 'react-icons/im';
|
import { ImExit } from 'react-icons/im';
|
||||||
import ResetPasswordModal from './ResetPasswordModal';
|
import ResetPasswordModal from './ResetPasswordModal';
|
||||||
import ResetModal from './ResetModal';
|
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>>}) {
|
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" />
|
<div className="flex-spacer" />
|
||||||
|
|
||||||
|
<MenuDropDownWithButton>
|
||||||
<Menu>
|
|
||||||
<Menu.Label>Firewall Access</Menu.Label>
|
<Menu.Label>Firewall Access</Menu.Label>
|
||||||
<Menu.Item icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
<Menu.Item icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Menu.Label>Actions</Menu.Label>
|
<Menu.Label>Actions</Menu.Label>
|
||||||
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
||||||
|
</MenuDropDownWithButton>
|
||||||
</Menu>
|
|
||||||
<Space w="md" />
|
<Space w="md" />
|
||||||
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
|
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
|
||||||
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
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 { AppShell } from '@mantine/core';
|
||||||
import NavBar from './NavBar';
|
import NavBar from './NavBar';
|
||||||
import FooterPage from './Footer';
|
import FooterPage from './Footer';
|
||||||
import HeaderPage from './Header';
|
import HeaderPage from './Header';
|
||||||
import { getmainpath } from '../js/utils';
|
import { getmainpath } from '../js/utils';
|
||||||
import { useLocation } from 'react-router-dom';
|
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 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>
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
|||||||
autostart: true
|
autostart: true
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
name: (value) => value !== ""?true:false,
|
name: (value) => value !== "" ? null : "Service name is required",
|
||||||
port: (value) => value>0 && value<65536,
|
port: (value) => (value>0 && value<65536) ? null : "Invalid port",
|
||||||
proto: (value) => ["tcp","udp"].includes(value),
|
proto: (value) => ["tcp","udp"].includes(value) ? null : "Invalid protocol",
|
||||||
ip_int: (value) => value.match(regex_ipv6)?true:false || value.match(regex_ipv4)?true:false
|
ip_int: (value) => (value.match(regex_ipv6) || value.match(regex_ipv4)) ? null : "Invalid IP address",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
|||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: { name:service.name },
|
initialValues: { name:service.name },
|
||||||
validate:{ name: (value) => value !== "" }
|
validate:{ name: (value) => value !== ""? null : "Service name is required" }
|
||||||
})
|
})
|
||||||
|
|
||||||
const close = () =>{
|
const close = () =>{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { errorNotify, okNotify, regex_ipv4 } from '../../../js/utils';
|
|||||||
import { BsTrashFill } from 'react-icons/bs';
|
import { BsTrashFill } from 'react-icons/bs';
|
||||||
import { BiRename } from 'react-icons/bi'
|
import { BiRename } from 'react-icons/bi'
|
||||||
import RenameForm from './RenameForm';
|
import RenameForm from './RenameForm';
|
||||||
|
import { MenuDropDownWithButton } from '../../MainLayout';
|
||||||
|
|
||||||
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
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" /></>
|
<><Space w="xl" /><Space w="xl" /></>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<div className="center-flex">
|
<div className="center-flex">
|
||||||
<Menu>
|
<MenuDropDownWithButton>
|
||||||
<Menu.Label><b>Rename service</b></Menu.Label>
|
<Menu.Label><b>Rename service</b></Menu.Label>
|
||||||
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||||
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</Menu>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
|
|||||||
@@ -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 React, { useEffect, useState } from "react"
|
||||||
import { getipinterfaces } from "../js/utils";
|
import { getipinterfaces } from "../js/utils";
|
||||||
import PortInput from "./PortInput";
|
import PortInput from "./PortInput";
|
||||||
import { UseFormReturnType } from "@mantine/form/lib/types";
|
import { UseFormReturnType } from "@mantine/form/lib/types";
|
||||||
|
|
||||||
interface ItemProps extends AutocompleteItem {
|
interface ItemProps extends AutocompleteItem {
|
||||||
label: string;
|
netint: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
|
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
|
||||||
({ label, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
|
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
|
||||||
( <b>{label}</b> ) -{">"} <b>{value}</b>
|
( <b>{netint}</b> ) -{">"} <b>{value}</b>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export default function PortAndInterface({ form, int_name, port_name, label }:{
|
|||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
getipinterfaces().then(data => {
|
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?<>
|
{label?<>
|
||||||
<Title order={6}>{label}</Title>
|
<Title order={6}>{label}</Title>
|
||||||
<Space h="xs" /></> :null}
|
<Space h="xs" /></> :null}
|
||||||
|
<div className='center-flex' style={{width:"100%"}}>
|
||||||
<div className='center-flex' style={{width:"100%"}}>
|
<Select
|
||||||
<Autocomplete
|
placeholder="10.1.1.1"
|
||||||
placeholder="10.1.1.1"
|
itemComponent={AutoCompleteItem}
|
||||||
itemComponent={AutoCompleteItem}
|
data={ipInterfaces}
|
||||||
data={ipInterfaces}
|
searchable
|
||||||
{...form.getInputProps(int_name)}
|
dropdownPosition="bottom"
|
||||||
style={{width:"100%"}}
|
maxDropdownHeight={100}
|
||||||
/>
|
creatable
|
||||||
<Space w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" />
|
getCreateLabel={(query) => `+ Use this: ${query}`}
|
||||||
<PortInput {...form.getInputProps(port_name)} />
|
onCreate={(query) => {
|
||||||
</div>
|
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>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
|||||||
autostart: false,
|
autostart: false,
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
name: (value) => value !== ""?true:false,
|
name: (value) => value !== ""? null : "Service name is required",
|
||||||
public_port: (value) => value>0 && value<65536,
|
public_port: (value) => (value>0 && value<65536) ? null : "Invalid public port",
|
||||||
proxy_port: (value) => value>0 && value<65536,
|
proxy_port: (value) => (value>0 && value<65536) ? null : "Invalid proxy port",
|
||||||
proto: (value) => ["tcp","udp"].includes(value),
|
proto: (value) => ["tcp","udp"].includes(value) ? null : "Invalid protocol",
|
||||||
ip_src: (value) => value.match(regex_ipv6_no_cidr)?true:false || value.match(regex_ipv4_no_cidr)?true:false,
|
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)?true:false || value.match(regex_ipv4_no_cidr)?true:false
|
ip_dst: (value) => (value.match(regex_ipv6_no_cidr) || value.match(regex_ipv4_no_cidr)) ? null : "Invalid destination IP address",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ function ChangeDestination({ opened, onClose, service }:{ opened:boolean, onClos
|
|||||||
proxy_port:service.proxy_port
|
proxy_port:service.proxy_port
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
proxy_port: (value) => value>0 && value<65536,
|
proxy_port: (value) => (value>0 && value<65536) ? null : "Invalid proxy port",
|
||||||
ip_dst: (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) || value.match(regex_ipv4_no_cidr))? null : "Invalid destination IP address",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
|||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: { name:service.name },
|
initialValues: { name:service.name },
|
||||||
validate:{ name: (value) => value !== "" }
|
validate:{ name: (value) => value !== ""? null : "Service name is required" }
|
||||||
})
|
})
|
||||||
|
|
||||||
const close = () =>{
|
const close = () =>{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import RenameForm from './RenameForm';
|
|||||||
import ChangeDestination from './ChangeDestination';
|
import ChangeDestination from './ChangeDestination';
|
||||||
import PortInput from '../../PortInput';
|
import PortInput from '../../PortInput';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
|
import { MenuDropDownWithButton } from '../../MainLayout';
|
||||||
|
|
||||||
function ServiceRow({ service }:{ service:Service }) {
|
function ServiceRow({ service }:{ service:Service }) {
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ function ServiceRow({ service }:{ service:Service }) {
|
|||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: { proxy_port:service.proxy_port },
|
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}) => {
|
const onChangeProxyPort = ({proxy_port}:{proxy_port:number}) => {
|
||||||
@@ -132,7 +133,7 @@ function ServiceRow({ service }:{ service:Service }) {
|
|||||||
|
|
||||||
<Space w="xl" /><Space w="xl" />
|
<Space w="xl" /><Space w="xl" />
|
||||||
<div className="center-flex">
|
<div className="center-flex">
|
||||||
<Menu>
|
<MenuDropDownWithButton>
|
||||||
<Menu.Label><b>Rename service</b></Menu.Label>
|
<Menu.Label><b>Rename service</b></Menu.Label>
|
||||||
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
||||||
<Menu.Label><b>Change destination</b></Menu.Label>
|
<Menu.Label><b>Change destination</b></Menu.Label>
|
||||||
@@ -140,7 +141,7 @@ function ServiceRow({ service }:{ service:Service }) {
|
|||||||
<Divider />
|
<Divider />
|
||||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||||
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</Menu>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
|||||||
autostart: true
|
autostart: true
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
name: (value) => value !== ""?true:false,
|
name: (value) => value !== ""? null : "Service name is required",
|
||||||
port: (value) => value>0 && value<65536,
|
port: (value) => (value>0 && value<65536) ? null : "Invalid port",
|
||||||
internalPort: (value) => value>0 && value<65536,
|
internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:
|
|||||||
port: service.public_port
|
port: service.public_port
|
||||||
},
|
},
|
||||||
validate:{
|
validate:{
|
||||||
internalPort: (value) => value>0 && value<65536,
|
internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port",
|
||||||
port: (value) => value>0 && value<65536
|
port: (value) => (value>0 && value<65536) ? null : "Invalid public port",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
|||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: { name:service.name },
|
initialValues: { name:service.name },
|
||||||
validate:{ name: (value) => value !== "" }
|
validate:{ name: (value) => value !== ""? null : "Service name is required" }
|
||||||
})
|
})
|
||||||
|
|
||||||
const close = () =>{
|
const close = () =>{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { BiRename } from 'react-icons/bi'
|
|||||||
import ChangePortModal from './ChangePortModal';
|
import ChangePortModal from './ChangePortModal';
|
||||||
import RenameForm from './RenameForm';
|
import RenameForm from './RenameForm';
|
||||||
import { regexproxy, Service } from '../utils';
|
import { regexproxy, Service } from '../utils';
|
||||||
|
import { MenuDropDownWithButton } from '../../MainLayout';
|
||||||
|
|
||||||
//"status":"stop"/"wait"/"active"/"pause",
|
//"status":"stop"/"wait"/"active"/"pause",
|
||||||
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
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" /></>
|
<><Space w="xl" /><Space w="xl" /></>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<div className="center-flex">
|
<div className="center-flex">
|
||||||
<Menu>
|
<MenuDropDownWithButton>
|
||||||
<Menu.Label><b>Rename service</b></Menu.Label>
|
<Menu.Label><b>Rename service</b></Menu.Label>
|
||||||
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
||||||
<Divider />
|
<Divider />
|
||||||
@@ -149,7 +150,7 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
|
|||||||
<Divider />
|
<Divider />
|
||||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||||
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</Menu>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
{["pause","wait"].includes(service.status)?
|
{["pause","wait"].includes(service.status)?
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user