Update route and add on update peer (#205)

This commit is contained in:
Sarooj bukhari
2023-06-15 13:28:55 +05:00
committed by GitHub
parent addd348456
commit 09e6de74ee
7 changed files with 2363 additions and 1088 deletions

View File

@@ -7,7 +7,7 @@ import {
Col,
Collapse,
Divider,
Drawer,
message,
Form,
Input,
Radio,
@@ -36,6 +36,8 @@ import { RuleObject } from "antd/lib/form";
import { useGetTokenSilently } from "../utils/token";
import { timeAgo } from "../utils/common";
import { actions as routeActions } from "../store/route";
import RouteAddNew from "./RouteAddNew";
import { Route } from "../store/route/types";
import {useGetGroupTagHelpers} from "../utils/groups";
const { Paragraph } = Typography;
@@ -65,7 +67,13 @@ const PeerUpdate = () => {
const updatedPeers = useSelector(
(state: RootState) => state.peer.updatedPeer
);
const savedRoute = useSelector((state: RootState) => state.route.savedRoute);
const deletedRoute = useSelector(
(state: RootState) => state.route.deletedRoute
);
const setupNewRouteVisible = useSelector(
(state: RootState) => state.route.setupNewRouteVisible
);
const [tagGroups, setTagGroups] = useState([] as string[]);
const [selectedTagGroups, setSelectedTagGroups] = useState([] as string[]);
const [peerGroups, setPeerGroups] = useState([] as GroupPeer[]);
@@ -85,6 +93,8 @@ const PeerUpdate = () => {
} as PeerGroupsToSave);
const routes = useSelector((state: RootState) => state.route.data);
const [form] = Form.useForm();
const styleNotification = { marginTop: 85 };
useEffect(() => {
//Unmounting component clean
@@ -274,6 +284,20 @@ const PeerUpdate = () => {
setCallingPeerAPI(false);
setSubmitRunning(false);
setEstimatedName("");
dispatch(routeActions.setSetupNewRouteVisible(false));
dispatch(
routeActions.setRoute({
network: "",
network_id: "",
description: "",
peer: "",
masquerade: true,
metric: 9999,
enabled: true,
groups: [],
} as Route)
);
};
const noUpdateToGroups = (): Boolean => {
@@ -432,6 +456,90 @@ const PeerUpdate = () => {
setFormPeer({ ...formPeer, login_expiration_enabled: checked });
};
const onClickAddNewRoute = () => {
dispatch(routeActions.setSetupNewRouteVisible(true));
dispatch(
routeActions.setRoute({
network: "",
network_id: "",
description: "",
peer: "",
masquerade: true,
metric: 9999,
enabled: true,
groups: [],
} as Route)
);
};
const saveKey = "saving";
useEffect(() => {
if (savedRoute.loading) {
message.loading({
content: "Saving...",
key: saveKey,
duration: 0,
style: styleNotification,
});
} else if (savedRoute.success) {
message.success({
content: "Route has been successfully updated.",
key: saveKey,
duration: 2,
style: styleNotification,
});
dispatch(routeActions.setSetupNewRouteVisible(false));
dispatch(routeActions.setSavedRoute({ ...savedRoute, success: false }));
dispatch(routeActions.resetSavedRoute(null));
} else if (savedRoute.error) {
let errorMsg = "Failed to update network route";
switch (savedRoute.error.statusCode) {
case 403:
errorMsg =
"Failed to update network route. You might not have enough permissions.";
break;
default:
errorMsg = savedRoute.error.data.message
? savedRoute.error.data.message
: errorMsg;
break;
}
message.error({
content: errorMsg,
key: saveKey,
duration: 5,
style: styleNotification,
});
dispatch(routeActions.setSavedRoute({ ...savedRoute, error: null }));
dispatch(routeActions.resetSavedRoute(null));
}
}, [savedRoute]);
const deleteKey = "deleting";
useEffect(() => {
const style = { marginTop: 85 };
if (deletedRoute.loading) {
message.loading({ content: "Deleting...", key: deleteKey, style });
} else if (deletedRoute.success) {
message.success({
content: "Route has been successfully deleted.",
key: deleteKey,
duration: 2,
style,
});
dispatch(routeActions.resetDeletedRoute(null));
} else if (deletedRoute.error) {
message.error({
content:
"Failed to remove route. You might not have enough permissions.",
key: deleteKey,
duration: 2,
style,
});
dispatch(routeActions.resetDeletedRoute(null));
}
}, [deletedRoute]);
return (
<>
{peer && (
@@ -460,71 +568,71 @@ const PeerUpdate = () => {
>
<Row gutter={16}>
<Col span={24}>
<Row align="top">
<Col flex="auto">
{!editName && peer.id && formPeer.name ? (
<div
style={{
color: "rgba(0, 0, 0, 0.88)",
fontWeight: "600",
fontSize: "16px",
}}
onClick={() => toggleEditName(true, peer.name)}
>
{formPeer.name ? formPeer.name : peer.name}
<EditOutlined style={{ marginLeft: "10px" }} />
<Row align="top">
<Col flex="auto">
{!editName && peer.id && formPeer.name ? (
<div
style={{
color: "rgba(0, 0, 0, 0.88)",
fontWeight: "600",
fontSize: "16px",
}}
onClick={() => toggleEditName(true, peer.name)}
>
{formPeer.name ? formPeer.name : peer.name}
<EditOutlined style={{ marginLeft: "10px" }} />
<Paragraph
type={"secondary"}
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontWeight: "400",
}}
<Paragraph
type={"secondary"}
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontWeight: "400",
}}
>
{formPeer.userEmail}{" "}
</Paragraph>
</div>
) : (
<Row>
<Space direction={"vertical"} size="small">
<Form.Item
name="name"
label="Name"
style={{ margin: "1px" }}
rules={[
{
required: true,
message:
"Please add a new name for this peer",
whitespace: true,
},
{ validator: nameValidator },
]}
>
{formPeer.userEmail}{" "}
</Paragraph>
</div>
) : (
<Row>
<Space direction={"vertical"} size="small">
<Form.Item
name="name"
label="Name"
style={{ margin: "1px" }}
rules={[
{
required: true,
message:
"Please add a new name for this peer",
whitespace: true,
},
{ validator: nameValidator },
]}
>
<Input
placeholder={peer.name}
ref={inputNameRef}
onPressEnter={() => toggleEditName(false)}
onBlur={() => toggleEditName(false)}
autoComplete="off"
max={59}
/>
</Form.Item>
<Form.Item
label="Domain name preview"
tooltip="If the domain name already exists, we add an increment number suffix to it"
style={{ margin: "1px" }}
>
<Paragraph>
<Tag>{estimatedName}</Tag>
</Paragraph>
</Form.Item>
</Space>
</Row>
)}
</Col>
</Row>
<Input
placeholder={peer.name}
ref={inputNameRef}
onPressEnter={() => toggleEditName(false)}
onBlur={() => toggleEditName(false)}
autoComplete="off"
max={59}
/>
</Form.Item>
<Form.Item
label="Domain name preview"
tooltip="If the domain name already exists, we add an increment number suffix to it"
style={{ margin: "1px" }}
>
<Paragraph>
<Tag>{estimatedName}</Tag>
</Paragraph>
</Form.Item>
</Space>
</Row>
)}
</Col>
</Row>
</Col>
</Row>
<Row gutter={30} style={{ marginTop: "25px" }}>
@@ -558,16 +666,16 @@ const PeerUpdate = () => {
</Col>
<Col span={5}>
<Form.Item
name="dns_label"
label="Domain name"
style={{ fontWeight: "bold" }}
name="dns_label"
label="Domain name"
style={{ fontWeight: "bold" }}
>
<Input
disabled={true}
value={formPeer.userEmail}
style={{ color: "#8c8c8c" }}
autoComplete="off"
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
disabled={true}
value={formPeer.userEmail}
style={{ color: "#8c8c8c" }}
autoComplete="off"
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
/>
</Form.Item>
</Col>
@@ -714,7 +822,9 @@ const PeerUpdate = () => {
style={{ marginTop: "-16px" }}
>
{peerRoutes && peerRoutes.length > 0 && (
<Button type="primary">Add route</Button>
<Button type="primary" onClick={onClickAddNewRoute}>
Add route
</Button>
)}
</Col>
</Row>
@@ -783,7 +893,9 @@ const PeerUpdate = () => {
>
You don't have any routes yet
</Paragraph>
<Button type="primary">Create route</Button>
<Button type="primary" onClick={onClickAddNewRoute}>
Add route
</Button>
</Space>
)}
</div>
@@ -799,16 +911,19 @@ const PeerUpdate = () => {
>
<Panel
key="0"
header={<Paragraph
header={
<Paragraph
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontSize: "16px",
fontWeight: "bold",
margin: "0",
}}
>
System info
</Paragraph>}
>
System info
</Paragraph>
}
className="system-info-panel"
>
<Row gutter={16}>
@@ -827,9 +942,7 @@ const PeerUpdate = () => {
>
Hostname:
</Text>
<Text type="secondary">
{formPeer.hostname}
</Text>
<Text type="secondary">{formPeer.hostname}</Text>
</Col>
<Col
span={24}
@@ -855,17 +968,15 @@ const PeerUpdate = () => {
}}
>
<Text
style={{
width: "100%",
maxWidth: "130px",
display: "inline-block",
}}
style={{
width: "100%",
maxWidth: "130px",
display: "inline-block",
}}
>
Agent version:
</Text>
<Text type="secondary">
{formPeer.version}
</Text>
<Text type="secondary">{formPeer.version}</Text>
</Col>
{formPeer.ui_version && (
<Col
@@ -883,9 +994,7 @@ const PeerUpdate = () => {
>
UI version:
</Text>
<Text type={"secondary"}>
{formPeer.ui_version}
</Text>
<Text type={"secondary"}>{formPeer.ui_version}</Text>
</Col>
)}
</Row>
@@ -895,6 +1004,7 @@ const PeerUpdate = () => {
</Card>
</Container>
)}
{setupNewRouteVisible && <RouteAddNew peer={peer} />}
</>
);
};

View File

@@ -0,0 +1,840 @@
import React, { useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "typesafe-actions";
import { actions as routeActions } from "../store/route";
import {
Button,
Col,
Divider,
Collapse,
Form,
Input,
InputNumber,
Radio,
Row,
Select,
SelectProps,
Space,
Switch,
Modal,
Typography,
} from "antd";
import {
CloseOutlined,
FlagFilled,
QuestionCircleFilled,
} from "@ant-design/icons";
import { Route, RouteToSave } from "../store/route/types";
import { Header } from "antd/es/layout/layout";
import { RuleObject } from "antd/lib/form";
import cidrRegex from "cidr-regex";
import {
initPeerMaps,
masqueradeDisabledMSG,
peerToPeerIP,
routePeerSeparator,
transformGroupedDataTable,
} from "../utils/routes";
import { useGetTokenSilently } from "../utils/token";
import { useGetGroupTagHelpers } from "../utils/groups";
const { Paragraph, Text } = Typography;
const { Panel } = Collapse;
interface FormRoute extends Route {}
const RouteAddNew = (selectedPeer: any) => {
const {
blueTagRender,
handleChangeTags,
dropDownRender,
optionRender,
tagGroups,
getExistingAndToCreateGroupsLists,
getGroupNamesFromIDs,
selectValidator,
} = useGetGroupTagHelpers();
// const { optionRender, blueTagRender, grayTagRender } =
// useGetGroupTagHelpers();
const { Option } = Select;
const { getTokenSilently } = useGetTokenSilently();
const dispatch = useDispatch();
const setupNewRouteVisible = useSelector(
(state: RootState) => state.route.setupNewRouteVisible
);
const setupNewRouteHA = useSelector(
(state: RootState) => state.route.setupNewRouteHA
);
const peers = useSelector((state: RootState) => state.peer.data);
const route = useSelector((state: RootState) => state.route.route);
const routes = useSelector((state: RootState) => state.route.data);
const savedRoute = useSelector((state: RootState) => state.route.savedRoute);
const [previousRouteKey, setPreviousRouteKey] = useState("");
const [editName, setEditName] = useState(false);
const [editDescription, setEditDescription] = useState(false);
const options: SelectProps["options"] = [];
const [formRoute, setFormRoute] = useState({} as FormRoute);
const [form] = Form.useForm();
const inputNameRef = useRef<any>(null);
const inputDescriptionRef = useRef<any>(null);
const defaultRoutingPeerMSG = "Routing Peer";
const [routingPeerMSG, setRoutingPeerMSG] = useState(defaultRoutingPeerMSG);
const defaultMasqueradeMSG = "Masquerade";
const [masqueradeMSG, setMasqueradeMSG] = useState(defaultMasqueradeMSG);
const defaultStatusMSG = "Status";
const [statusMSG, setStatusMSG] = useState(defaultStatusMSG);
const [peerNameToIP, peerIPToName, peerIPToID] = initPeerMaps(peers);
const [newRoute, setNewRoute] = useState(false);
useEffect(() => {
if (!newRoute) {
setRoutingPeerMSG(defaultRoutingPeerMSG);
setMasqueradeMSG("Update Masquerade");
setStatusMSG("Update Status");
} else {
setRoutingPeerMSG(defaultRoutingPeerMSG);
setMasqueradeMSG(defaultMasqueradeMSG);
setStatusMSG(defaultStatusMSG);
setPreviousRouteKey("");
}
}, [newRoute]);
useEffect(() => {
if (editName)
inputNameRef.current!.focus({
cursor: "end",
});
}, [editName]);
useEffect(() => {
if (editDescription)
inputDescriptionRef.current!.focus({
cursor: "end",
});
}, [editDescription]);
useEffect(() => {
if (!route) return;
if (selectedPeer && selectedPeer.peer) {
options?.push({
label: peerToPeerIP(selectedPeer.peer.name, selectedPeer.peer.ip),
value: peerToPeerIP(selectedPeer.peer.name, selectedPeer.peer.ip),
disabled: false,
});
const udpateRoute = { ...route, peer: options[0].value } as FormRoute;
setFormRoute(udpateRoute);
form.setFieldsValue(udpateRoute);
setPreviousRouteKey(udpateRoute.network_id + udpateRoute.network);
} else {
const fRoute = {
...route,
groups: getGroupNamesFromIDs(route.groups),
} as FormRoute;
setFormRoute(fRoute);
setPreviousRouteKey(fRoute.network_id + fRoute.network);
form.setFieldsValue(fRoute);
}
if (!route.network_id) {
setNewRoute(true);
} else {
setNewRoute(false);
}
}, [route]);
if (!selectedPeer.peer) {
peers.forEach((p) => {
let os: string;
os = p.os;
if (
!os.toLowerCase().startsWith("darwin") &&
!os.toLowerCase().startsWith("windows") &&
!os.toLowerCase().startsWith("android") &&
route &&
!routes
.filter((r) => r.network_id === route.network_id)
.find((r) => r.peer === p.id)
) {
options?.push({
label: peerToPeerIP(p.name, p.ip),
value: peerToPeerIP(p.name, p.ip),
disabled: false,
});
}
});
}
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
let peerIDList = inputRoute.peer.split(routePeerSeparator);
let peerID: string;
if (peerIDList.length === 1) {
peerID = inputRoute.peer;
} else {
if (peerIDList[1]) {
peerID = peerIPToID[peerIDList[1]];
} else {
peerID = peerIPToID[peerNameToIP[inputRoute.peer]];
}
}
let [existingGroups, groupsToCreate] = getExistingAndToCreateGroupsLists(
inputRoute.groups
);
return {
id: inputRoute.id,
network: inputRoute.network,
network_id: inputRoute.network_id,
description: inputRoute.description,
peer: peerID,
enabled: inputRoute.enabled,
masquerade: inputRoute.masquerade,
metric: inputRoute.metric,
groups: existingGroups,
groupsToCreate: groupsToCreate,
} as RouteToSave;
};
const handleFormSubmit = () => {
form
.validateFields()
.then(() => {
if (!setupNewRouteHA || formRoute.peer != "") {
const routeToSave = createRouteToSave(formRoute);
dispatch(
routeActions.saveRoute.request({
getAccessTokenSilently: getTokenSilently,
payload: routeToSave,
})
);
} else {
let groupedDataTable = transformGroupedDataTable(routes, peers);
groupedDataTable.forEach((group) => {
if (group.key == previousRouteKey) {
group.groupedRoutes.forEach((route) => {
let updateRoute: FormRoute = {
...formRoute,
id: route.id,
peer: route.peer,
metric: route.metric,
enabled:
formRoute.enabled != group.enabled
? formRoute.enabled
: route.enabled,
};
const routeToSave = createRouteToSave(updateRoute);
dispatch(
routeActions.saveRoute.request({
getAccessTokenSilently: getTokenSilently,
payload: routeToSave,
})
);
});
}
});
}
})
.catch((errorInfo) => {
console.log("errorInfo", errorInfo);
});
};
const setVisibleNewRoute = (status: boolean) => {
dispatch(routeActions.setSetupNewRouteVisible(status));
};
const setSetupNewRouteHA = (status: boolean) => {
dispatch(routeActions.setSetupNewRouteHA(status));
};
const onCancel = () => {
if (savedRoute.loading) return;
setEditName(false);
dispatch(
routeActions.setRoute({
network: "",
network_id: "",
description: "",
peer: "",
metric: 9999,
masquerade: false,
enabled: true,
groups: [],
} as Route)
);
setVisibleNewRoute(false);
setSetupNewRouteHA(false);
setPreviousRouteKey("");
setNewRoute(false);
};
const onChange = (data: any) => {
setFormRoute({ ...formRoute, ...data });
};
const peerDropDownRender = (menu: React.ReactElement) => <>{menu}</>;
const toggleEditName = (status: boolean) => {
setEditName(status);
};
const toggleEditDescription = (status: boolean) => {
setEditDescription(status);
};
const networkRangeValidator = (_: RuleObject, value: string) => {
if (!cidrRegex().test(value)) {
return Promise.reject(
new Error("Please enter a valid CIDR, e.g. 192.168.1.0/24")
);
}
if (Number(value.split("/")[1]) < 7) {
return Promise.reject(
new Error("Please enter a network mask larger than /7")
);
}
return Promise.resolve();
};
const peerValidator = (_: RuleObject, value: string) => {
if (value == "" && newRoute) {
return Promise.reject(new Error("Please select routing one peer"));
}
return Promise.resolve();
};
const selectPreValidator = (obj: RuleObject, value: string[]) => {
if (setupNewRouteHA && formRoute.peer == "") {
let [, newGroups] = getExistingAndToCreateGroupsLists(value);
if (newGroups.length > 0) {
return Promise.reject(
new Error(
"You can't add new Groups from the group update view, please remove:\"" +
newGroups +
'"'
)
);
}
}
return selectValidator(obj, value);
};
const handleMasqueradeChange = (checked: boolean) => {
setFormRoute({
...formRoute,
masquerade: checked,
});
};
const handleEnableChange = (checked: boolean) => {
setFormRoute({
...formRoute,
enabled: checked,
});
};
return (
<>
{route && (
<Modal
open={setupNewRouteVisible}
onCancel={onCancel}
footer={
<Space style={{ display: "flex", justifyContent: "end" }}>
<Button onClick={onCancel} disabled={savedRoute.loading}>
Cancel
</Button>
<Button
type="primary"
disabled={savedRoute.loading}
onClick={handleFormSubmit}
>
Add Route
</Button>
</Space>
}
>
<Form
layout="vertical"
form={form}
requiredMark={false}
onValuesChange={onChange}
>
<Row gutter={16}>
<Col span={24}>
<Header
style={{
border: "none",
}}
>
<Paragraph
style={{
textAlign: "start",
whiteSpace: "pre-line",
fontSize: "18px",
margin: "0px",
marginBottom: "15px",
}}
>
Add Route
</Paragraph>
{!!selectedPeer.peer && (
<div style={{ lineHeight: "20px" }}>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Routing Peer
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
<Form.Item
name="peer"
rules={[{ validator: peerValidator }]}
>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
disabled={!!selectedPeer.peer}
/>
</Form.Item>
</div>
)}
<Row align="top">
<Col span={24} style={{ lineHeight: "20px" }}>
{!editName && formRoute.id ? (
<div
className={
"access-control input-text ant-drawer-title"
}
onClick={() => toggleEditName(true)}
>
{formRoute.id ? formRoute.network_id : "New Route"}
</div>
) : (
<>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Network Identifier
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Add a unique cryptographic key that is assigned to
each device
</Paragraph>
<Form.Item
name="network_id"
label=""
style={{marginBottom:"10px"}}
rules={[
{
required: true,
message:
"Please add an identifier for this access route",
whitespace: true,
},
]}
>
<Input
placeholder="for example “e.g. aws-eu-central-1-vpc”"
ref={inputNameRef}
disabled={!setupNewRouteHA && !newRoute}
onPressEnter={() => toggleEditName(false)}
onBlur={() => toggleEditName(false)}
autoComplete="off"
maxLength={40}
/>
</Form.Item>
</>
)}
{!editDescription ? (
<div
onClick={() => toggleEditDescription(true)}
style={{
margin: "0 0 30px",
lineHeight: "22px",
cursor: "pointer",
}}
>
{formRoute.description &&
formRoute.description.trim() !== "" ? (
formRoute.description
) : (
<span style={{ textDecoration: "underline" }}>
Add description
</span>
)}
</div>
) : (
<Form.Item
name="description"
label="Description"
style={{ marginTop: 24 }}
>
<Input
placeholder="Add description..."
ref={inputDescriptionRef}
disabled={!setupNewRouteHA && !newRoute}
onPressEnter={() => toggleEditDescription(false)}
onBlur={() => toggleEditDescription(false)}
autoComplete="off"
maxLength={200}
/>
</Form.Item>
)}
</Col>
</Row>
<Row align="top">
<Col flex="auto"></Col>
</Row>
</Header>
</Col>
{/* {!!!selectedPeer.peer && (
<Col span={24}>
<Form.Item name="enabled" label="">
<div
style={{
display: "flex",
gap: "15px",
}}
>
<Switch
size={"small"}
checked={formRoute.enabled}
onChange={handleEnableChange}
/>
<div>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Enabled
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "0",
}}
>
You can enable or disable the route
</Paragraph>
</div>
</div>
</Form.Item>
</Col>
)} */}
<Col span={24}>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Network Range
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Add a private IP address range
</Paragraph>
<Form.Item
name="network"
label=""
rules={[{ validator: networkRangeValidator }]}
>
<Input
placeholder="for example “172.16.0.0/16”"
disabled={!setupNewRouteHA && !newRoute}
autoComplete="off"
minLength={9}
maxLength={43}
/>
</Form.Item>
</Col>
{!!!selectedPeer.peer && (
<Col span={24}>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Routing Peer
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
<Form.Item name="peer" rules={[{ validator: peerValidator }]}>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
disabled={!!selectedPeer.peer}
/>
</Form.Item>
</Col>
)}
<Col span={24}>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Distribution groups
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Advertise this route to peers that belong to the following
groups
</Paragraph>
<Form.Item
name="groups"
label=""
rules={[{ validator: selectPreValidator }]}
>
<Select
mode="tags"
style={{ width: "100%" }}
placeholder="Associate groups with the network route"
tagRender={blueTagRender}
onChange={handleChangeTags}
dropdownRender={dropDownRender}
>
{tagGroups.map((m) => (
<Option key={m}>{optionRender(m)}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="enabled" label="">
<div
style={{
display: "flex",
gap: "15px",
}}
>
<Switch
size={"small"}
checked={formRoute.enabled}
onChange={handleEnableChange}
/>
<div>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Enabled
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "0",
}}
>
You can enable or disable the route
</Paragraph>
</div>
</div>
</Form.Item>
</Col>
<Col span={24}>
<Collapse
onChange={onChange}
bordered={false}
ghost={true}
style={{ padding: "0" }}
>
<Panel
key="0"
header={
<Paragraph
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontSize: "14px",
fontWeight: "400",
margin: "0",
textDecoration: "underline",
}}
>
More settings
</Paragraph>
}
className="system-info-panel"
>
<Row gutter={16} style={{padding:"15px 0 0"}}>
<Col span={22}>
<Form.Item name="masquerade" label="">
<div
style={{
display: "flex",
gap: "15px",
}}
>
<Switch
size={"small"}
disabled={!setupNewRouteHA && !newRoute}
checked={formRoute.masquerade}
onChange={handleMasqueradeChange}
/>
<div>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Masquerade
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "0",
}}
>
Allow access to your private networks without
configuring routes on your local routers or
other devices.
</Paragraph>
</div>
</div>
</Form.Item>
</Col>
<Col span={24}>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "bold",
}}
>
Metric
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "500",
marginBottom: "5px",
}}
>
Lower metrics indicating higher priority routes
</Paragraph>
<Row>
<Col span={12}>
<Form.Item name="metric" label="">
<InputNumber
min={1}
max={9999}
autoComplete="off"
className="w-100"
/>
</Form.Item>
</Col>
</Row>
</Col>
</Row>
</Panel>
</Collapse>
</Col>
<Col
span={24}
style={{ marginTop: "20px", marginBottom: "25px" }}
>
<Text type={"secondary"}>
Learn more about
<a
target="_blank"
rel="noreferrer"
href="https://docs.netbird.io/how-to/routing-traffic-to-private-networks"
>
{" "}
Network Routes
</a>
</Text>
</Col>
</Row>
</Form>
</Modal>
)}
</>
);
};
export default RouteAddNew;

File diff suppressed because it is too large Load Diff

View File

@@ -197,7 +197,7 @@ td.non-highlighted-table-column {
}
.tag-box .ant-select-selector {
padding: 0 5px!important;
padding: 0 5px !important;
}
.tag-box .ant-select-selection-item {
@@ -219,4 +219,8 @@ td.non-highlighted-table-column {
align-items: center;
margin-top: 3px;
text-align: center;
}
.w-100 {
width: 100%;
}

View File

@@ -28,6 +28,7 @@ const actions = {
setRoute: createAction('SET_ROUTE')<Route>(),
setSetupNewRouteVisible: createAction('SET_SETUP_NEW_ROUTE_VISIBLE')<boolean>(),
setSetupEditRouteVisible: createAction('SET_SETUP_EDIT_ROUTE_VISIBLE')<boolean>(),
setSetupNewRouteHA: createAction('SET_SETUP_NEW_ROUTE_HA')<boolean>()
};

View File

@@ -13,7 +13,8 @@ type StateType = Readonly<{
deleteRoute: DeleteResponse<string | null>;
savedRoute: CreateResponse<Route | null>;
setupNewRouteVisible: boolean;
setupNewRouteHA: boolean
setupNewRouteHA: boolean;
setupEditRouteVisible: boolean;
}>;
const initialState: StateType = {
@@ -27,17 +28,18 @@ const initialState: StateType = {
success: false,
failure: false,
error: null,
data : null
data: null,
},
savedRoute: <CreateResponse<Route | null>>{
loading: false,
success: false,
failure: false,
error: null,
data : null
data: null,
},
setupNewRouteVisible: false,
setupNewRouteHA: false
setupNewRouteHA: false,
setupEditRouteVisible: false,
};
const data = createReducer<Route[], ActionTypes>(initialState.data as Route[])
@@ -79,6 +81,13 @@ const savedRoute = createReducer<CreateResponse<Route | null>, ActionTypes>(init
const setupNewRouteVisible = createReducer<boolean, ActionTypes>(initialState.setupNewRouteVisible)
.handleAction(actions.setSetupNewRouteVisible, (store, action) => action.payload)
const setupEditRouteVisible = createReducer<boolean, ActionTypes>(
initialState.setupEditRouteVisible
).handleAction(
actions.setSetupEditRouteVisible,
(store, action) => action.payload
);
const setupNewRouteHA = createReducer<boolean, ActionTypes>(initialState.setupNewRouteHA)
.handleAction(actions.setSetupNewRouteHA, (store, action) => action.payload)
@@ -91,5 +100,6 @@ export default combineReducers({
deletedRoute,
savedRoute,
setupNewRouteVisible,
setupNewRouteHA
setupNewRouteHA,
setupEditRouteVisible,
});

File diff suppressed because it is too large Load Diff