mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f854b01a0 | ||
|
|
303d51eff8 | ||
|
|
21e69e642a | ||
|
|
835bb37ab9 | ||
|
|
a944dc8ab0 | ||
|
|
b2c51533fb | ||
|
|
fd24536926 | ||
|
|
8e8484cd45 | ||
|
|
6c87f53195 | ||
|
|
9bbbff7dc0 | ||
|
|
04a20fa31f | ||
|
|
3797db93f0 | ||
|
|
2e81765e85 | ||
|
|
cb9f76c0fc | ||
|
|
54accb665c |
No files matched your search
Generated
+6
-6
@@ -63,9 +63,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
|
||||
"integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g=="
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz",
|
||||
"integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg=="
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.0",
|
||||
@@ -17173,9 +17173,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@adobe/css-tools": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
|
||||
"integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g=="
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz",
|
||||
"integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg=="
|
||||
},
|
||||
"@ampproject/remapping": {
|
||||
"version": "2.2.0",
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"auth0Auth": "true",
|
||||
"authAuthority": "https://netbird-localdev.eu.auth0.com",
|
||||
"authClientId": "kBRMAOqIZ7hvpVCaypQLCJvTzkYYIXVt",
|
||||
"authScopesSupported": "openid profile email api offline_access email_verified",
|
||||
"authAudience": "http://localhost:3000/",
|
||||
|
||||
"apiOrigin": "http://0.0.0.0",
|
||||
"grpcApiOrigin": "http://0.0.0.0:80",
|
||||
"latestVersion": "v0.6.3"
|
||||
}
|
||||
@@ -284,7 +284,7 @@ const AccessControlNew = () => {
|
||||
<Divider style={{ margin: "8px 0" }} />
|
||||
<Row style={{ padding: "0 8px 4px" }}>
|
||||
<Col flex="auto">
|
||||
<Text type={"secondary"}>Add new ports by pressing "Enter"</Text>
|
||||
<Text type={"secondary"}>Add new groups by pressing "Enter"</Text>
|
||||
</Col>
|
||||
<Col flex="none">
|
||||
<svg
|
||||
|
||||
+208
-125
@@ -35,6 +35,7 @@ import {
|
||||
LockOutlined,
|
||||
EditOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
ExclamationCircleFilled,
|
||||
} from "@ant-design/icons";
|
||||
import { RuleObject } from "antd/lib/form";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
@@ -145,10 +146,22 @@ const PeerUpdate = (props: any) => {
|
||||
|
||||
useEffect(() => {
|
||||
setPeerRoutes([]);
|
||||
const temp: any[] = [];
|
||||
if (peer && peer.groups) {
|
||||
peer?.groups?.forEach((pg: any) => {
|
||||
routes.forEach((route: any) => {
|
||||
if (route.peer_groups?.includes(pg.id)) {
|
||||
temp.push(route);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const filterPeerRoutes: any = routes.filter(
|
||||
(route) => route.peer === peer.id
|
||||
);
|
||||
setPeerRoutes(filterPeerRoutes);
|
||||
let mergeArr: any = [...filterPeerRoutes, ...temp];
|
||||
setPeerRoutes(mergeArr);
|
||||
const filterNotPeerRoutes: any = routes.filter(
|
||||
(route) => route.peer !== peer.id
|
||||
);
|
||||
@@ -179,6 +192,15 @@ const PeerUpdate = (props: any) => {
|
||||
|
||||
useEffect(() => {}, [users]);
|
||||
|
||||
const routeAddAllowed = (os: string): boolean => {
|
||||
return (
|
||||
os !== "" &&
|
||||
!os.toLowerCase().startsWith("darwin") &&
|
||||
!os.toLowerCase().startsWith("windows") &&
|
||||
!os.toLowerCase().startsWith("android")
|
||||
);
|
||||
};
|
||||
|
||||
const toggleEditName = (status: boolean, value?: string) => {
|
||||
setEditName(status);
|
||||
|
||||
@@ -389,13 +411,13 @@ const PeerUpdate = (props: any) => {
|
||||
const style = { marginTop: 85 };
|
||||
if (savedGroups.loading) {
|
||||
message.loading({
|
||||
content: "Updating peer groups...",
|
||||
content: "Updating peer group...",
|
||||
key: saveGroupsKey,
|
||||
style,
|
||||
});
|
||||
} else if (savedGroups.success) {
|
||||
message.success({
|
||||
content: "Peer groups have been successfully updated.",
|
||||
content: "Peer group have been successfully updated.",
|
||||
key: saveGroupsKey,
|
||||
duration: 2,
|
||||
style,
|
||||
@@ -408,7 +430,7 @@ const PeerUpdate = (props: any) => {
|
||||
} else if (savedGroups.error) {
|
||||
message.error({
|
||||
content:
|
||||
"Failed to update peer groups. You might not have enough permissions.",
|
||||
"Failed to update peer group. You might not have enough permissions.",
|
||||
key: saveGroupsKey,
|
||||
duration: 2,
|
||||
style,
|
||||
@@ -652,6 +674,48 @@ const PeerUpdate = (props: any) => {
|
||||
}
|
||||
}, [deletedRoute]);
|
||||
|
||||
const renderGroupRouting = (rowGroups: string[] | null) => {
|
||||
let groupsMap = new Map<string, Group>();
|
||||
groups.forEach((g) => {
|
||||
groupsMap.set(g.id!, g);
|
||||
});
|
||||
|
||||
let displayGroups: Group[] = [];
|
||||
if (rowGroups) {
|
||||
displayGroups = rowGroups
|
||||
.filter((g) => groupsMap.get(g))
|
||||
.map((g) => groupsMap.get(g)!);
|
||||
}
|
||||
|
||||
const groupToCompare =
|
||||
peer &&
|
||||
peer.groups &&
|
||||
peer?.groups.map((element1) => {
|
||||
return element1.id;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="gp-main-wrapper">
|
||||
{displayGroups &&
|
||||
displayGroups.length > 0 &&
|
||||
displayGroups.map((group) => {
|
||||
if (group.id && !groupToCompare?.includes(group?.id)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="g-r-wrapper">
|
||||
<span className="f-r-name">
|
||||
<Tag color={"blue"} style={{ marginRight: 3 }}>
|
||||
{group.name}
|
||||
</Tag>
|
||||
</span>{" "}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{peer && (
|
||||
@@ -965,132 +1029,151 @@ const PeerUpdate = (props: any) => {
|
||||
{/* --- */}
|
||||
{!isGroupUpdateView && (
|
||||
<>
|
||||
<Card
|
||||
bordered={true}
|
||||
// loading={loading}Ć’
|
||||
style={{ marginBottom: "7px" }}
|
||||
>
|
||||
<div style={{ maxWidth: "800px" }}>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "left",
|
||||
whiteSpace: "pre-line",
|
||||
fontSize: "16px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Network routes
|
||||
</Paragraph>
|
||||
<Row
|
||||
gutter={21}
|
||||
style={{ marginTop: "-16px", marginBottom: "10px" }}
|
||||
>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={20}
|
||||
lg={20}
|
||||
xl={20}
|
||||
xxl={20}
|
||||
span={20}
|
||||
{routeAddAllowed(peer.os) && (
|
||||
<Card
|
||||
bordered={true}
|
||||
// loading={loading}Ć’
|
||||
style={{ marginBottom: "7px" }}
|
||||
>
|
||||
<div style={{ maxWidth: "800px" }}>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "left",
|
||||
whiteSpace: "pre-line",
|
||||
fontSize: "16px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{ textAlign: "left", whiteSpace: "pre-line" }}
|
||||
Network routes
|
||||
</Paragraph>
|
||||
<Row
|
||||
gutter={21}
|
||||
style={{ marginTop: "-16px", marginBottom: "10px" }}
|
||||
>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={20}
|
||||
lg={20}
|
||||
xl={20}
|
||||
xxl={20}
|
||||
span={20}
|
||||
>
|
||||
Access other networks without installing NetBird on
|
||||
every resource.
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={1}
|
||||
lg={1}
|
||||
xl={1}
|
||||
xxl={1}
|
||||
span={1}
|
||||
style={{ marginTop: "-16px" }}
|
||||
>
|
||||
{peerRoutes && peerRoutes.length > 0 && (
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{ textAlign: "left", whiteSpace: "pre-line" }}
|
||||
>
|
||||
Access other networks without installing NetBird on
|
||||
every resource.
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={1}
|
||||
lg={1}
|
||||
xl={1}
|
||||
xxl={1}
|
||||
span={1}
|
||||
style={{ marginTop: "-16px" }}
|
||||
>
|
||||
{peerRoutes && peerRoutes.length > 0 && (
|
||||
<Button type="primary" onClick={onClickAddNewRoute}>
|
||||
Add route
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{peerRoutes && peerRoutes.length > 0 && (
|
||||
<Table
|
||||
size={"small"}
|
||||
style={{ marginTop: "-10px" }}
|
||||
showHeader={false}
|
||||
scroll={{ x: 800 }}
|
||||
pagination={false}
|
||||
dataSource={peerRoutes}
|
||||
>
|
||||
<Column title="Name" dataIndex="network_id" />
|
||||
<Column title="Name" dataIndex="network" />
|
||||
<Column
|
||||
title="enabled"
|
||||
dataIndex="network"
|
||||
render={(e, record: any, index) => {
|
||||
return record.peer_groups ? (
|
||||
renderGroupRouting(record.peer_groups)
|
||||
) : (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={record.enabled}
|
||||
size="small"
|
||||
onChange={(checked) =>
|
||||
onRouteEnableChange(checked, record)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Column
|
||||
align="right"
|
||||
render={(text, record: any, index) => {
|
||||
return record.peer_groups ? (
|
||||
<Tooltip
|
||||
color="#fff"
|
||||
overlayClassName="peer-avail-tooltip"
|
||||
title={`Peer "${formPeer.name}" is a part of a group used in a network route.
|
||||
To remove this peer from the network route, you need to disassociate
|
||||
this peer from the groups used in this route.`}
|
||||
>
|
||||
<Button type={"text"} disabled={true}>
|
||||
Delete
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Button
|
||||
danger={true}
|
||||
type={"text"}
|
||||
onClick={() => {
|
||||
showConfirmDelete(
|
||||
record.id,
|
||||
record.network_id
|
||||
);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
<Divider style={{ marginTop: "-12px" }}></Divider>
|
||||
{(peerRoutes === null || peerRoutes.length === 0) && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="start"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "35px 0px",
|
||||
marginTop: "-40px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{ textAlign: "start", whiteSpace: "pre-line" }}
|
||||
>
|
||||
You don't have any routes yet
|
||||
</Paragraph>
|
||||
<Button type="primary" onClick={onClickAddNewRoute}>
|
||||
Add route
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{peerRoutes && peerRoutes.length > 0 && (
|
||||
<Table
|
||||
size={"small"}
|
||||
style={{ marginTop: "-10px" }}
|
||||
showHeader={false}
|
||||
scroll={{ x: 800 }}
|
||||
pagination={false}
|
||||
dataSource={peerRoutes}
|
||||
>
|
||||
<Column title="Name" dataIndex="network_id" />
|
||||
<Column title="Name" dataIndex="network" />
|
||||
<Column
|
||||
title="enabled"
|
||||
dataIndex="network"
|
||||
render={(e, record: any, index) => {
|
||||
return (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={record.enabled}
|
||||
size="small"
|
||||
onChange={(checked) =>
|
||||
onRouteEnableChange(checked, record)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Column
|
||||
align="right"
|
||||
render={(text, record: any, index) => {
|
||||
return (
|
||||
<Button
|
||||
danger={true}
|
||||
type={"text"}
|
||||
onClick={() => {
|
||||
showConfirmDelete(record.id, record.network_id);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
<Divider style={{ marginTop: "-12px" }}></Divider>
|
||||
{(peerRoutes === null || peerRoutes.length === 0) && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="start"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "35px 0px",
|
||||
marginTop: "-40px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{ textAlign: "start", whiteSpace: "pre-line" }}
|
||||
>
|
||||
You don't have any routes yet
|
||||
</Paragraph>
|
||||
<Button type="primary" onClick={onClickAddNewRoute}>
|
||||
Add route
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Card bordered={true} style={{ marginBottom: "50px" }}>
|
||||
<Col span={24}>
|
||||
|
||||
+163
-77
@@ -17,7 +17,9 @@ import {
|
||||
Switch,
|
||||
Modal,
|
||||
Typography,
|
||||
Tabs,
|
||||
} from "antd";
|
||||
import type { TabsProps } from "antd";
|
||||
import CreatableSelect from "react-select/creatable";
|
||||
import { Route, RouteToSave } from "../store/route/types";
|
||||
import { Header } from "antd/es/layout/layout";
|
||||
@@ -38,6 +40,7 @@ const { Panel } = Collapse;
|
||||
interface FormRoute extends Route {}
|
||||
|
||||
const RouteAddNew = (selectedPeer: any) => {
|
||||
const [activeTab, setActiveTab] = useState<string | null>(null);
|
||||
const {
|
||||
blueTagRender,
|
||||
handleChangeTags,
|
||||
@@ -75,6 +78,10 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
const [peerNameToIP, peerIPToName, peerIPToID] = initPeerMaps(peers);
|
||||
const [newRoute, setNewRoute] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (setupNewRouteVisible) setActiveTab("routingPeer");
|
||||
}, [setupNewRouteVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editName)
|
||||
inputNameRef.current!.focus({
|
||||
@@ -159,6 +166,12 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
}
|
||||
|
||||
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
|
||||
if (inputRoute.peer_groups) {
|
||||
inputRoute = {
|
||||
...inputRoute,
|
||||
peer_groups: [inputRoute.peer_groups[inputRoute.peer_groups.length - 1]],
|
||||
};
|
||||
}
|
||||
let peerIDList = inputRoute.peer.split(routePeerSeparator);
|
||||
let peerID: string;
|
||||
if (peerIDList.length === 1) {
|
||||
@@ -175,24 +188,70 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
inputRoute.groups
|
||||
);
|
||||
|
||||
return {
|
||||
const payload = {
|
||||
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;
|
||||
|
||||
if (activeTab === "routingPeer") {
|
||||
let pay = { ...payload, peer: peerID };
|
||||
return pay;
|
||||
}
|
||||
|
||||
if (activeTab === "groupOfPeers") {
|
||||
if (inputRoute.peer_groups) {
|
||||
let [currentPeersGroup, peerGroupsToCreate] =
|
||||
getExistingAndToCreateGroupsLists(inputRoute.peer_groups);
|
||||
|
||||
let pay = {
|
||||
...payload,
|
||||
peer_groups: currentPeersGroup,
|
||||
peerGroupsToCreate: peerGroupsToCreate,
|
||||
};
|
||||
return pay;
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
};
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then(() => {
|
||||
const t = routes.filter((route) => {
|
||||
if (
|
||||
route.network_id === formRoute.network_id &&
|
||||
route.network === formRoute.network
|
||||
) {
|
||||
return route;
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
formRoute.peer_groups &&
|
||||
formRoute.peer_groups.length > 0 &&
|
||||
t &&
|
||||
t.length > 0
|
||||
) {
|
||||
const style = { marginTop: 85 };
|
||||
const duplicateNetworkIdKey = "duplicateKey";
|
||||
return message.error({
|
||||
content:
|
||||
"A route with this network identifier and network range already exists. Please use a different network identifier or network range.",
|
||||
key: duplicateNetworkIdKey,
|
||||
duration: 5,
|
||||
style,
|
||||
});
|
||||
}
|
||||
|
||||
if (!setupNewRouteHA || formRoute.peer != "") {
|
||||
const routeToSave = createRouteToSave(formRoute);
|
||||
dispatch(
|
||||
@@ -243,6 +302,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
|
||||
const onCancel = () => {
|
||||
if (savedRoute.loading) return;
|
||||
setActiveTab(null);
|
||||
setEditName(false);
|
||||
dispatch(
|
||||
routeActions.setRoute({
|
||||
@@ -254,6 +314,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
masquerade: false,
|
||||
enabled: true,
|
||||
groups: [],
|
||||
peer_groups: [],
|
||||
} as Route)
|
||||
);
|
||||
setVisibleNewRoute(false);
|
||||
@@ -300,8 +361,15 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const peerGroupsValidaton = (_: RuleObject, value: string) => {
|
||||
if (value.length < 1) {
|
||||
return Promise.reject(new Error("Please select a peer group"));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const selectPreValidator = (obj: RuleObject, value: string[]) => {
|
||||
if (setupNewRouteHA && formRoute.peer == "") {
|
||||
if (setupNewRouteHA && formRoute.peer === "") {
|
||||
let [, newGroups] = getExistingAndToCreateGroupsLists(value);
|
||||
if (newGroups.length > 0) {
|
||||
return Promise.reject(
|
||||
@@ -361,56 +429,95 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
const styleNotification = { marginTop: 85 };
|
||||
const onTabChange = (key: string) => {
|
||||
setActiveTab(key);
|
||||
};
|
||||
|
||||
const saveKey = "saving";
|
||||
useEffect(() => {
|
||||
if (savedRoute.loading) {
|
||||
message.loading({
|
||||
content: "Saving...",
|
||||
key: saveKey,
|
||||
duration: 0,
|
||||
style: styleNotification,
|
||||
const handleSingleChangeTags = (values: any) => {
|
||||
const lastValue = values[values.length - 1];
|
||||
if (values.length > 0) {
|
||||
form.setFieldsValue({
|
||||
peer_groups: [lastValue],
|
||||
});
|
||||
} else if (savedRoute.success) {
|
||||
message.success({
|
||||
content: "Route has been successfully added.",
|
||||
key: saveKey,
|
||||
duration: 2,
|
||||
style: styleNotification,
|
||||
});
|
||||
dispatch(routeActions.setSetupNewRouteVisible(false));
|
||||
dispatch(routeActions.setSetupEditRouteVisible(false));
|
||||
dispatch(routeActions.setSetupEditRoutePeerVisible(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 items: TabsProps["items"] = [
|
||||
{
|
||||
key: "routingPeer",
|
||||
label: "Routing Peer",
|
||||
children: (
|
||||
<>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign a peer as a routing peer for the Network CIDR
|
||||
</Paragraph>
|
||||
{activeTab === "routingPeer" && (
|
||||
<Form.Item name="peer" rules={[{ validator: peerValidator }]}>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select Peer"
|
||||
dropdownRender={peerDropDownRender}
|
||||
options={options}
|
||||
allowClear={true}
|
||||
disabled={!!selectedPeer.selectedPeer}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}{" "}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "groupOfPeers",
|
||||
label: "Peer group",
|
||||
children: (
|
||||
<>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign peer group with Linux machines to be used as routing peers
|
||||
</Paragraph>
|
||||
{activeTab === "groupOfPeers" && (
|
||||
<Form.Item
|
||||
name="peer_groups"
|
||||
rules={[{ validator: peerGroupsValidaton }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleSingleChangeTags}
|
||||
dropdownRender={dropDownRender}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{route && (
|
||||
{route && setupNewRouteVisible && (
|
||||
<Modal
|
||||
open={setupNewRouteVisible}
|
||||
onCancel={onCancel}
|
||||
@@ -558,7 +665,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Add a unique cryptographic key that is assigned
|
||||
Add a unique network identifier that is assigned
|
||||
to each device
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
@@ -667,36 +774,15 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
|
||||
{!!!selectedPeer.selectedPeer && (
|
||||
<Col span={24}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Routing Peer
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
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.selectedPeer}
|
||||
{activeTab ? (
|
||||
<Tabs
|
||||
defaultActiveKey={activeTab}
|
||||
items={items}
|
||||
onChange={onTabChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
<Col span={24}>
|
||||
|
||||
@@ -17,10 +17,13 @@ import {
|
||||
Switch,
|
||||
Card,
|
||||
Typography,
|
||||
Tabs,
|
||||
} from "antd";
|
||||
import type { TabsProps } from "antd";
|
||||
import { Route, RouteToSave } from "../store/route/types";
|
||||
import { Header } from "antd/es/layout/layout";
|
||||
import { RuleObject } from "antd/lib/form";
|
||||
import { isEmpty } from "lodash";
|
||||
import {
|
||||
initPeerMaps,
|
||||
peerToPeerIP,
|
||||
@@ -61,6 +64,16 @@ const RoutePeerUpdate = () => {
|
||||
const [form] = Form.useForm();
|
||||
const inputDescriptionRef = useRef<any>(null);
|
||||
const [peerNameToIP, peerIPToName, peerIPToID] = initPeerMaps(peers);
|
||||
const [activeTab, setActiveTab] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(formRoute))
|
||||
if (formRoute.peer_groups) {
|
||||
setActiveTab("groupOfPeers");
|
||||
} else {
|
||||
setActiveTab("routingPeer");
|
||||
}
|
||||
}, [formRoute]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editDescription)
|
||||
@@ -100,7 +113,25 @@ const RoutePeerUpdate = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const handleSingleChangeTags = (values: any) => {
|
||||
const lastValue = values[values.length - 1];
|
||||
if (values.length > 0) {
|
||||
form.setFieldsValue({
|
||||
peer_groups: [lastValue],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
|
||||
if (inputRoute.peer_groups) {
|
||||
inputRoute = {
|
||||
...inputRoute,
|
||||
peer_groups: [
|
||||
inputRoute.peer_groups[inputRoute.peer_groups.length - 1],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
let peerIDList = inputRoute.peer.split(routePeerSeparator);
|
||||
let peerID: string;
|
||||
if (peerIDList.length === 1) {
|
||||
@@ -116,18 +147,38 @@ const RoutePeerUpdate = () => {
|
||||
inputRoute.groups
|
||||
);
|
||||
|
||||
return {
|
||||
const payload = {
|
||||
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;
|
||||
|
||||
if (activeTab === "routingPeer") {
|
||||
let pay = { ...payload, peer: peerID };
|
||||
return pay;
|
||||
}
|
||||
|
||||
if (activeTab === "groupOfPeers") {
|
||||
if (inputRoute.peer_groups) {
|
||||
let [currentPeersGroup, peerGroupsToCreate] =
|
||||
getExistingAndToCreateGroupsLists(inputRoute.peer_groups);
|
||||
|
||||
let pay = {
|
||||
...payload,
|
||||
peer_groups: currentPeersGroup,
|
||||
peerGroupsToCreate: peerGroupsToCreate,
|
||||
};
|
||||
return pay;
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
};
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
@@ -165,6 +216,7 @@ const RoutePeerUpdate = () => {
|
||||
masquerade: false,
|
||||
enabled: true,
|
||||
groups: [],
|
||||
peer_groups: [],
|
||||
} as Route)
|
||||
);
|
||||
setVisibleNewRoute(false);
|
||||
@@ -269,6 +321,95 @@ const RoutePeerUpdate = () => {
|
||||
}
|
||||
}, [savedRoute]);
|
||||
|
||||
const onTabChange = (key: string) => {
|
||||
console.log(key);
|
||||
// setActiveTab(key);
|
||||
};
|
||||
|
||||
const items: TabsProps["items"] = [
|
||||
{
|
||||
key: "routingPeer",
|
||||
label: "Routing Peer",
|
||||
disabled: true,
|
||||
children: (
|
||||
<>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign a peer as a routing peer for the Network CIDR
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="peer"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select routing one peer",
|
||||
},
|
||||
]}
|
||||
style={{ maxWidth: "400px" }}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select Peer"
|
||||
dropdownRender={peerDropDownRender}
|
||||
options={options}
|
||||
allowClear={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "groupOfPeers",
|
||||
label: "Peer group",
|
||||
disabled: true,
|
||||
children: (
|
||||
<>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign peer group with Linux machines to be used as routing peers
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="peer_groups"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select peer group",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ maxWidth: "400px" }}
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleSingleChangeTags}
|
||||
dropdownRender={dropDownRender}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container style={{ paddingTop: "40px", paddingBottom: "50px" }}>
|
||||
@@ -409,39 +550,13 @@ const RoutePeerUpdate = () => {
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Routing Peer
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign a peer as a routing peer for the Network CIDR
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="peer"
|
||||
rules={[{ validator: peerValidator }]}
|
||||
style={{ maxWidth: "400px" }}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select Peer"
|
||||
dropdownRender={peerDropDownRender}
|
||||
options={options}
|
||||
allowClear={true}
|
||||
{activeTab && (
|
||||
<Tabs
|
||||
defaultActiveKey={activeTab}
|
||||
items={items}
|
||||
onChange={onTabChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<label
|
||||
|
||||
+134
-85
@@ -110,6 +110,14 @@ const RouteAddNew = () => {
|
||||
});
|
||||
|
||||
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
|
||||
if (inputRoute.peer_groups) {
|
||||
inputRoute = {
|
||||
...inputRoute,
|
||||
peer_groups: [
|
||||
inputRoute.peer_groups[inputRoute.peer_groups.length - 1],
|
||||
],
|
||||
};
|
||||
}
|
||||
let peerIDList = inputRoute.peer.split(routePeerSeparator);
|
||||
let peerID: string;
|
||||
if (peerIDList.length === 1) {
|
||||
@@ -126,20 +134,47 @@ const RouteAddNew = () => {
|
||||
inputRoute.groups
|
||||
);
|
||||
|
||||
return {
|
||||
const payload = {
|
||||
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;
|
||||
|
||||
if (inputRoute.peer_groups) {
|
||||
let [currentPeersGroup, peerGroupsToCreate] =
|
||||
getExistingAndToCreateGroupsLists(inputRoute.peer_groups);
|
||||
|
||||
let pay = {
|
||||
...payload,
|
||||
peer_groups: currentPeersGroup,
|
||||
peerGroupsToCreate: peerGroupsToCreate,
|
||||
};
|
||||
return pay;
|
||||
}
|
||||
|
||||
if (inputRoute.peer) {
|
||||
let pay = { ...payload, peer: peerID };
|
||||
return pay;
|
||||
}
|
||||
|
||||
return payload;
|
||||
};
|
||||
|
||||
const handleSingleChangeTags = (values: any) => {
|
||||
const lastValue = values[values.length - 1];
|
||||
if (values.length > 0) {
|
||||
form.setFieldsValue({
|
||||
peer_groups: [lastValue],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
form
|
||||
.validateFields()
|
||||
@@ -232,7 +267,7 @@ const RouteAddNew = () => {
|
||||
};
|
||||
|
||||
const selectPreValidator = (obj: RuleObject, value: string[]) => {
|
||||
if (setupNewRouteHA && formRoute.peer == "") {
|
||||
if (setupNewRouteHA && formRoute.peer == "" && !formRoute.peer_groups) {
|
||||
let [, newGroups] = getExistingAndToCreateGroupsLists(value);
|
||||
if (newGroups.length > 0) {
|
||||
return Promise.reject(
|
||||
@@ -247,53 +282,6 @@ const RouteAddNew = () => {
|
||||
return selectValidator(obj, value);
|
||||
};
|
||||
|
||||
const styleNotification = { marginTop: 85 };
|
||||
|
||||
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 added.",
|
||||
key: saveKey,
|
||||
duration: 2,
|
||||
style: styleNotification,
|
||||
});
|
||||
dispatch(routeActions.setSetupNewRouteVisible(false));
|
||||
dispatch(routeActions.setSetupEditRouteVisible(false));
|
||||
dispatch(routeActions.setSetupEditRoutePeerVisible(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]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{route && (
|
||||
@@ -310,7 +298,7 @@ const RouteAddNew = () => {
|
||||
disabled={savedRoute.loading}
|
||||
onClick={handleFormSubmit}
|
||||
>
|
||||
Add route
|
||||
{formRoute.peer_groups ? "Save" : "Add route"}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
@@ -337,7 +325,9 @@ const RouteAddNew = () => {
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Add new routing peer
|
||||
{formRoute.peer_groups
|
||||
? "Configure peer group"
|
||||
: "Add new routing peer"}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
@@ -350,8 +340,9 @@ const RouteAddNew = () => {
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
When you add multiple routing peers, NetBird enables high
|
||||
availability
|
||||
{formRoute.peer_groups
|
||||
? "Add peer group with multiple peers to enable high availability"
|
||||
: "When you add multiple routing peers, NetBird enables high availability"}
|
||||
</Paragraph>
|
||||
|
||||
<Row align="top">
|
||||
@@ -377,7 +368,6 @@ const RouteAddNew = () => {
|
||||
Network name and CIDR that you are adding the route to
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
// name="network_id"
|
||||
label=""
|
||||
rules={[
|
||||
{
|
||||
@@ -411,36 +401,95 @@ const RouteAddNew = () => {
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Routing Peer
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign a routing peer to the network. This peer has to reside
|
||||
in the network
|
||||
</Paragraph>
|
||||
<Form.Item name="peer" rules={[{ validator: peerValidator }]}>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select Peer"
|
||||
dropdownRender={peerDropDownRender}
|
||||
options={options}
|
||||
allowClear={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
{formRoute.peer_groups ? (
|
||||
<>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Peer group
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign peer group with Linux machines to be used as
|
||||
routing peers
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="peer_groups"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select peer group",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ maxWidth: "100%" }}
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleSingleChangeTags}
|
||||
dropdownRender={dropDownRender}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Routing Peer
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
Assign a routing peer to the network. This peer has to
|
||||
reside in the network
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="peer"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select routing one peer",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select Peer"
|
||||
dropdownRender={peerDropDownRender}
|
||||
options={options}
|
||||
allowClear={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<label
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Tag,
|
||||
Typography,
|
||||
Card,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import {
|
||||
@@ -54,7 +55,7 @@ const SetupKeyNew = (props: any) => {
|
||||
const savedSetupKey = useSelector(
|
||||
(state: RootState) => state.setupKey.savedSetupKey
|
||||
);
|
||||
|
||||
|
||||
const groups = useSelector((state: RootState) => state.group.data);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
@@ -91,14 +92,13 @@ const SetupKeyNew = (props: any) => {
|
||||
.filter((g) => allGroups.get(g))
|
||||
.map((g) => allGroups.get(g)!.name);
|
||||
}
|
||||
|
||||
const fSetupKey = {
|
||||
...setupKey,
|
||||
autoGroupNames: setupKey.auto_groups || [],
|
||||
exp: moment(setupKey.expires),
|
||||
last: moment(setupKey.last_used),
|
||||
} as FormSetupKey;
|
||||
form.setFieldsValue(fSetupKey);
|
||||
form.setFieldsValue(fSetupKey);
|
||||
setFormSetupKey(fSetupKey);
|
||||
}, [setupKey]);
|
||||
|
||||
@@ -126,8 +126,7 @@ const SetupKeyNew = (props: any) => {
|
||||
await form.validateFields();
|
||||
} catch (e) {
|
||||
const errorFields = (e as any).errorFields;
|
||||
return console.log("errorInfo", errorFields);
|
||||
}
|
||||
}
|
||||
|
||||
const setupKeyToSave = createSetupKeyToSave();
|
||||
dispatch(
|
||||
@@ -258,8 +257,7 @@ const SetupKeyNew = (props: any) => {
|
||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null));
|
||||
setVisibleNewSetupKey(false);
|
||||
};
|
||||
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
{!isGroupUpdateView && (
|
||||
<Breadcrumb
|
||||
@@ -303,13 +301,9 @@ const SetupKeyNew = (props: any) => {
|
||||
{!isGroupUpdateView && (
|
||||
<Row style={{ marginTop: "10px" }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={11}
|
||||
lg={11}
|
||||
xl={11}
|
||||
xxl={11}
|
||||
span={11}
|
||||
md={8}
|
||||
lg={8}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
@@ -343,7 +337,14 @@ const SetupKeyNew = (props: any) => {
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Col
|
||||
sm={24}
|
||||
md={8}
|
||||
lg={6}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
@@ -358,8 +359,48 @@ const SetupKeyNew = (props: any) => {
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
{formSetupKey.type === "one-off" ? "One-off" : "Reusable"},
|
||||
available uses
|
||||
Type{" "}
|
||||
{formSetupKey.ephemeral ? (
|
||||
<Tooltip title="Peers that are offline for over 10 minutes will be removed automatically">
|
||||
<Tag>
|
||||
<Text type="secondary" style={{ fontSize: 10 }}>
|
||||
ephemeral
|
||||
</Text>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : (
|
||||
" "
|
||||
)}
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
disabled
|
||||
value={
|
||||
formSetupKey.type === "one-off" ? "One-off" : "Reusable"
|
||||
}
|
||||
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
|
||||
style={{ marginTop: "8px" }}
|
||||
/>
|
||||
</Col>
|
||||
</Col>
|
||||
|
||||
<Col sm={24} md={8} lg={3}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
{/* {formSetupKey.type === "one-off" ? "One-off" : "Reusable"}, */}
|
||||
Available uses
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
@@ -378,44 +419,6 @@ const SetupKeyNew = (props: any) => {
|
||||
</Row>
|
||||
)}
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={11}
|
||||
lg={11}
|
||||
xl={11}
|
||||
xxl={11}
|
||||
span={11}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
Ephemeral peers enabled?
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
disabled
|
||||
value={formSetupKey.ephemeral? "yes" : "no"}
|
||||
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
|
||||
style={{ marginTop: "8px" }}
|
||||
/>
|
||||
|
||||
</Col>
|
||||
</Col>
|
||||
{!isGroupUpdateView && (
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Paragraph style={{ margin: 0, fontWeight: "500" }}>
|
||||
@@ -434,54 +437,52 @@ const SetupKeyNew = (props: any) => {
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={!isGroupUpdateView ? 11 : 24}
|
||||
lg={!isGroupUpdateView ? 11 : 24}
|
||||
xl={!isGroupUpdateView ? 11 : 24}
|
||||
xxl={!isGroupUpdateView ? 11 : 24}
|
||||
span={!isGroupUpdateView ? 11 : 24}
|
||||
style={{
|
||||
paddingRight: `${!isGroupUpdateView ? "70px" : "0"}`,
|
||||
}}
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={!isGroupUpdateView ? 11 : 24}
|
||||
lg={!isGroupUpdateView ? 11 : 24}
|
||||
xl={!isGroupUpdateView ? 11 : 24}
|
||||
xxl={!isGroupUpdateView ? 11 : 24}
|
||||
span={!isGroupUpdateView ? 11 : 24}
|
||||
style={{
|
||||
paddingRight: `${!isGroupUpdateView ? "70px" : "0"}`,
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Auto-assigned groups
|
||||
</Paragraph>
|
||||
Auto-assigned groups
|
||||
</Paragraph>
|
||||
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
style={{ marginTop: "8px", marginBottom: 0 }}
|
||||
name="autoGroupNames"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
style={{ marginTop: "8px", marginBottom: 0 }}
|
||||
name="autoGroupNames"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Associate groups with the key"
|
||||
tagRender={blueTagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
optionFilterProp="searchValue"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Associate groups with the key"
|
||||
tagRender={blueTagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
// enabled only when we have a new key !setupkey.id or when the key is valid
|
||||
disabled={!(!setupKey.id || setupKey.valid)}
|
||||
optionFilterProp="searchValue"
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -505,7 +505,8 @@ const SetupKeyNew = () => {
|
||||
type={"secondary"}
|
||||
style={{ whiteSpace: "pre-line", margin: 0 }}
|
||||
>
|
||||
Use this type of key to add peers that will be auto removed after being inactive
|
||||
Peers that are offline for over 10 minutes will be removed
|
||||
automatically
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
|
||||
@@ -17,14 +17,16 @@ export const UbuntuTab = () => {
|
||||
key: 1,
|
||||
title: "Add repository",
|
||||
commands: [
|
||||
`sudo apt-get update`,
|
||||
`sudo apt install ca-certificates curl gnupg -y`,
|
||||
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
|
||||
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`,
|
||||
`curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg`,
|
||||
`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`,
|
||||
].join("\n"),
|
||||
commandsForCopy: [
|
||||
`sudo apt install ca-certificates curl gnupg -y`,
|
||||
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
|
||||
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`,
|
||||
`sudo apt-get update`,
|
||||
`sudo apt-get install ca-certificates curl gnupg -y`,
|
||||
`curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg`,
|
||||
`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
|
||||
+76
-4
@@ -513,10 +513,82 @@ ul.ant-list-items {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.d-none{
|
||||
display: none!important;
|
||||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nohover {
|
||||
background: transparent!important;
|
||||
cursor: text;
|
||||
background: transparent !important;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.emp-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.emp-wrapper p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.g-r-wrapper {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.gp-main-wrapper .g-r-wrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gp-main-wrapper {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.availtooltip a {
|
||||
padding: 5px 0 0;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.availtooltip {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.availtooltip p {
|
||||
color: #000;
|
||||
font-size: 13.5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.avail-inner {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.avail-inner span {
|
||||
color: #1890FF;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.avail-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.peer-avail-tooltip .ant-tooltip-inner {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.peer-avail-tooltip {
|
||||
min-width: 400px;
|
||||
}
|
||||
.info-icon {
|
||||
font-size: 22px;
|
||||
color: #FD9349;
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
import {ExpiresInValue} from "../../views/ExpiresInInput";
|
||||
|
||||
export interface Account {
|
||||
id: string;
|
||||
settings: { peer_login_expiration_enabled: boolean, peer_login_expiration: number}
|
||||
id: string;
|
||||
settings: {
|
||||
peer_login_expiration_enabled: boolean;
|
||||
peer_login_expiration: number;
|
||||
jwt_groups_enabled: boolean;
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FormAccount extends Account {
|
||||
peer_login_expiration_enabled: boolean
|
||||
peer_login_expiration_formatted : ExpiresInValue
|
||||
peer_login_expiration_enabled: boolean;
|
||||
jwt_groups_enabled: boolean;
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
peer_login_expiration_formatted: ExpiresInValue;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
export interface Event {
|
||||
id: string;
|
||||
timestamp: string
|
||||
activity: string
|
||||
activity_code: string
|
||||
initiator_id: string
|
||||
target_id: string
|
||||
meta: { [key: string]: string }
|
||||
id: string;
|
||||
timestamp: string;
|
||||
activity: string;
|
||||
activity_code: string;
|
||||
initiator_id: string;
|
||||
initiator_email: string;
|
||||
initiator_name: string;
|
||||
target_id: string;
|
||||
meta: { [key: string]: string };
|
||||
}
|
||||
@@ -56,12 +56,16 @@ export function* saveRoute(
|
||||
);
|
||||
|
||||
const routeToSave = action.payload.payload;
|
||||
|
||||
let groupsToCreate = routeToSave.groupsToCreate;
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = [];
|
||||
}
|
||||
|
||||
let peerGroupsToCreate = routeToSave.peerGroupsToCreate;
|
||||
if (!peerGroupsToCreate) {
|
||||
peerGroupsToCreate = [];
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(
|
||||
groupsToCreate.map((g) =>
|
||||
@@ -72,12 +76,28 @@ export function* saveRoute(
|
||||
)
|
||||
);
|
||||
|
||||
const responsesPeerGroup = yield all(
|
||||
peerGroupsToCreate.map((g) =>
|
||||
call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: { name: g },
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const resGroups = (responsesGroup as ApiResponse<Group>[])
|
||||
.filter((r) => r.statusCode === 200)
|
||||
.map((g) => g.body as Group)
|
||||
.map((g) => g.id);
|
||||
const newGroups = [...routeToSave.groups, ...resGroups];
|
||||
|
||||
|
||||
const resPeersGroups = (responsesPeerGroup as ApiResponse<Group>[])
|
||||
.filter((r) => r.statusCode === 200)
|
||||
.map((g) => g.body as Group)
|
||||
.map((g) => g.id);
|
||||
const newPeerGroups = [...routeToSave?.peer_groups || [], ...resPeersGroups];
|
||||
|
||||
const payloadToSave = {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: {
|
||||
@@ -89,6 +109,7 @@ export function* saveRoute(
|
||||
network: routeToSave.network,
|
||||
network_id: routeToSave.network_id,
|
||||
peer: routeToSave.peer,
|
||||
peer_groups: newPeerGroups,
|
||||
groups: newGroups,
|
||||
} as Route,
|
||||
};
|
||||
|
||||
+14
-10
@@ -1,18 +1,22 @@
|
||||
|
||||
export interface Route {
|
||||
id?: string | null
|
||||
description: string
|
||||
enabled: boolean
|
||||
peer: string
|
||||
network: string
|
||||
network_id: string
|
||||
network_type?: string
|
||||
metric?: number
|
||||
masquerade: boolean
|
||||
groups: string[]
|
||||
id?: string | null;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
peer: string;
|
||||
network: string;
|
||||
network_id: string;
|
||||
network_type?: string;
|
||||
metric?: number;
|
||||
masquerade: boolean;
|
||||
groups: string[];
|
||||
peer_groups?: string[];
|
||||
routesGroups?: string[];
|
||||
groupedRoutes?: Array<any>;
|
||||
}
|
||||
|
||||
export interface RouteToSave extends Route
|
||||
{
|
||||
groupsToCreate: string[]
|
||||
peerGroupsToCreate: string[]
|
||||
}
|
||||
+48
-30
@@ -1,54 +1,72 @@
|
||||
import {ActionType, createAction, createAsyncAction} from 'typesafe-actions';
|
||||
import {User, UserToSave} from './types';
|
||||
import {ApiError, CreateResponse, DeleteResponse, RequestPayload} from '../../services/api-client/types';
|
||||
import { ActionType, createAction, createAsyncAction } from "typesafe-actions";
|
||||
import { User, UserToSave } from "./types";
|
||||
import {
|
||||
ApiError,
|
||||
CreateResponse,
|
||||
DeleteResponse,
|
||||
RequestPayload,
|
||||
} from "../../services/api-client/types";
|
||||
|
||||
const actions = {
|
||||
getUsers: createAsyncAction(
|
||||
'GET_USERS_REQUEST',
|
||||
'GET_USERS_SUCCESS',
|
||||
'GET_USERS_FAILURE',
|
||||
"GET_USERS_REQUEST",
|
||||
"GET_USERS_SUCCESS",
|
||||
"GET_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
getServiceUsers: createAsyncAction(
|
||||
'GET_SERVICE_USERS_REQUEST',
|
||||
'GET_SERVICE_USERS_SUCCESS',
|
||||
'GET_SERVICE_USERS_FAILURE',
|
||||
"GET_SERVICE_USERS_REQUEST",
|
||||
"GET_SERVICE_USERS_SUCCESS",
|
||||
"GET_SERVICE_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
getRegularUsers: createAsyncAction(
|
||||
'GET_REGULAR_USERS_REQUEST',
|
||||
'GET_REGULAR_USERS_SUCCESS',
|
||||
'GET_REGULAR_USERS_FAILURE',
|
||||
"GET_REGULAR_USERS_REQUEST",
|
||||
"GET_REGULAR_USERS_SUCCESS",
|
||||
"GET_REGULAR_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
deleteUser: createAsyncAction(
|
||||
'DELETE_USER_REQUEST',
|
||||
'DELETE_USER_SUCCESS',
|
||||
'DELETE_USER_FAILURE',
|
||||
)<RequestPayload<string>, DeleteResponse<string | null>, DeleteResponse<string | null>>(),
|
||||
setDeletedUser: createAction('SET_DELETED_USER')<DeleteResponse<string | null>>(),
|
||||
resetDeletedUser: createAction('RESET_DELETED_USER')<null>(),
|
||||
"DELETE_USER_REQUEST",
|
||||
"DELETE_USER_SUCCESS",
|
||||
"DELETE_USER_FAILURE"
|
||||
)<
|
||||
RequestPayload<string>,
|
||||
DeleteResponse<string | null>,
|
||||
DeleteResponse<string | null>
|
||||
>(),
|
||||
setDeletedUser:
|
||||
createAction("SET_DELETED_USER")<DeleteResponse<string | null>>(),
|
||||
resetDeletedUser: createAction("RESET_DELETED_USER")<null>(),
|
||||
|
||||
// used to set a user object that was picked in the user table in the UserUpdate drawer (user update window on right-side).
|
||||
setUser: createAction('SET_USER')<User>(),
|
||||
setUser: createAction("SET_USER")<User>(),
|
||||
// used to make the UserUpdate drawer visible in the UI.
|
||||
setUpdateUserDrawerVisible: createAction('SET_UPDATE_USER_VISIBLE')<boolean>(),
|
||||
setUpdateUserDrawerVisible: createAction(
|
||||
"SET_UPDATE_USER_VISIBLE"
|
||||
)<boolean>(),
|
||||
// used to make the ViewUserPopup visible in the UI.
|
||||
setInviteUserPopupVisible: createAction('SET_INVITE_USER_VISIBLE')<boolean>(),
|
||||
setInviteUserPopupVisible: createAction("SET_INVITE_USER_VISIBLE")<boolean>(),
|
||||
// used to make the EditUserPopup visible in the UI.
|
||||
setEditUserPopupVisible: createAction('SET_EDIT_USER_VISIBLE')<boolean>(),
|
||||
setEditUserPopupVisible: createAction("SET_EDIT_USER_VISIBLE")<boolean>(),
|
||||
// used to make the AddServiceUserPopup visible in the UI.
|
||||
setAddServiceUserPopupVisible: createAction('SET_ADD_SERVICE_USER_VISIBLE')<boolean>(),
|
||||
setAddServiceUserPopupVisible: createAction(
|
||||
"SET_ADD_SERVICE_USER_VISIBLE"
|
||||
)<boolean>(),
|
||||
// used to remember what tab was open on users page
|
||||
setUserTabOpen: createAction('SET_USER_TAB_OPEN')<string>(),
|
||||
setUserTabOpen: createAction("SET_USER_TAB_OPEN")<string>(),
|
||||
|
||||
saveUser: createAsyncAction(
|
||||
'SAVE_USER_REQUEST',
|
||||
'SAVE_USER_SUCCESS',
|
||||
'SAVE_USER_FAILURE',
|
||||
)<RequestPayload<UserToSave>, CreateResponse<User | null>, CreateResponse<User | null>>(),
|
||||
setSavedUser: createAction('SET_SAVED_USER')<CreateResponse<User | null>>(),
|
||||
resetSavedUser: createAction('RESET_SAVED_USER')<null>(),
|
||||
"SAVE_USER_REQUEST",
|
||||
"SAVE_USER_SUCCESS",
|
||||
"SAVE_USER_FAILURE"
|
||||
)<
|
||||
RequestPayload<UserToSave>,
|
||||
CreateResponse<User | null>,
|
||||
CreateResponse<User | null>
|
||||
>(),
|
||||
setSavedUser: createAction("SET_SAVED_USER")<CreateResponse<User | null>>(),
|
||||
resetSavedUser: createAction("RESET_SAVED_USER")<null>(),
|
||||
};
|
||||
|
||||
export type ActionTypes = ActionType<typeof actions>;
|
||||
|
||||
+162
-138
@@ -1,159 +1,183 @@
|
||||
import {all, call, put, select, takeLatest} from 'redux-saga/effects';
|
||||
import {ApiError, ApiResponse, CreateResponse, DeleteResponse} from '../../services/api-client/types';
|
||||
import {User, UserToSave} from './types'
|
||||
import service from './service';
|
||||
import actions from './actions';
|
||||
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
||||
import {
|
||||
ApiError,
|
||||
ApiResponse,
|
||||
CreateResponse,
|
||||
DeleteResponse,
|
||||
} from "../../services/api-client/types";
|
||||
import { User, UserToSave } from "./types";
|
||||
import service from "./service";
|
||||
import actions from "./actions";
|
||||
import serviceGroup from "../group/service";
|
||||
import {Group} from "../group/types";
|
||||
import { Group } from "../group/types";
|
||||
|
||||
export function* getUsers(action: ReturnType<typeof actions.getUsers.request>): Generator {
|
||||
try {
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getUsers(
|
||||
action: ReturnType<typeof actions.getUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* getServiceUsers(action: ReturnType<typeof actions.getServiceUsers.request>): Generator {
|
||||
try {
|
||||
action.payload.queryParams = {service_user: true}
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getServiceUsers(
|
||||
action: ReturnType<typeof actions.getServiceUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
action.payload.queryParams = { service_user: true };
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getServiceUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getServiceUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getServiceUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getServiceUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* getRegularUsers(action: ReturnType<typeof actions.getRegularUsers.request>): Generator {
|
||||
try {
|
||||
action.payload.queryParams = {service_user: false}
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getRegularUsers(
|
||||
action: ReturnType<typeof actions.getRegularUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
action.payload.queryParams = { service_user: false };
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getRegularUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getRegularUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getRegularUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getRegularUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* saveUser(action: ReturnType<typeof actions.saveUser.request>): Generator {
|
||||
try {
|
||||
yield put(actions.setSavedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as CreateResponse<User | null>))
|
||||
export function* saveUser(
|
||||
action: ReturnType<typeof actions.saveUser.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield put(
|
||||
actions.setSavedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
|
||||
const userToSave = action.payload.payload
|
||||
const userToSave = action.payload.payload;
|
||||
|
||||
let groupsToCreate = userToSave.groupsToCreate
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = []
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(groupsToCreate.map(g => call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: {name: g}
|
||||
})
|
||||
))
|
||||
|
||||
const resGroups = (responsesGroup as ApiResponse<Group>[]).filter(r => r.statusCode === 200).map(g => (g.body as Group)).map(g => g.id)
|
||||
const newGroups = [...userToSave.auto_groups, ...resGroups]
|
||||
let payload = {
|
||||
name: userToSave.name,
|
||||
email: userToSave.email,
|
||||
role: userToSave.role,
|
||||
auto_groups: newGroups,
|
||||
is_service_user: userToSave.is_service_user,
|
||||
is_blocked: userToSave.is_blocked
|
||||
} as UserToSave
|
||||
|
||||
let effect
|
||||
if (!userToSave.id) {
|
||||
effect = yield call(service.createUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload
|
||||
});
|
||||
} else {
|
||||
payload.id = userToSave.id
|
||||
effect = yield call(service.editUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload
|
||||
});
|
||||
}
|
||||
const response = effect as ApiResponse<User>;
|
||||
|
||||
yield put(actions.saveUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as CreateResponse<User | null>));
|
||||
} catch (err) {
|
||||
yield put(actions.saveUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as CreateResponse<User | null>));
|
||||
let groupsToCreate = userToSave.groupsToCreate;
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = [];
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(
|
||||
groupsToCreate.map((g) =>
|
||||
call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: { name: g },
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const resGroups = (responsesGroup as ApiResponse<Group>[])
|
||||
.filter((r) => r.statusCode === 200)
|
||||
.map((g) => g.body as Group)
|
||||
.map((g) => g.id);
|
||||
const newGroups = [...userToSave.auto_groups, ...resGroups];
|
||||
let payload = {
|
||||
name: userToSave.name,
|
||||
email: userToSave.email,
|
||||
role: userToSave.role,
|
||||
auto_groups: newGroups,
|
||||
is_service_user: userToSave.is_service_user,
|
||||
is_blocked: userToSave.is_blocked,
|
||||
} as UserToSave;
|
||||
|
||||
let effect;
|
||||
if (!userToSave.id) {
|
||||
effect = yield call(service.createUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload,
|
||||
});
|
||||
} else {
|
||||
payload.id = userToSave.id;
|
||||
effect = yield call(service.editUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload,
|
||||
});
|
||||
}
|
||||
const response = effect as ApiResponse<User>;
|
||||
|
||||
yield put(
|
||||
actions.saveUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(
|
||||
actions.saveUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* deleteUser(action: ReturnType<typeof actions.deleteUser.request>): Generator {
|
||||
try {
|
||||
yield call(actions.setDeletedUser,{
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>)
|
||||
export function* deleteUser(
|
||||
action: ReturnType<typeof actions.deleteUser.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield put(
|
||||
actions.setDeletedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
|
||||
const effect = yield call(service.deleteUser, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
|
||||
yield put(actions.deleteUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as DeleteResponse<string | null>));
|
||||
|
||||
const users = (yield select(state => state.users.data)) as User[]
|
||||
const regularUsers = (yield select(state => state.users.regularUsers)) as User[]
|
||||
const serviceUsers = (yield select(state => state.users.serviceUsers)) as User[]
|
||||
yield put(actions.getUsers.success(users.filter((p:User) => p.id !== action.payload.payload)))
|
||||
yield put(actions.getRegularUsers.success(regularUsers.filter((p:User) => p.id !== action.payload.payload)))
|
||||
yield put(actions.getServiceUsers.success(serviceUsers.filter((p:User) => p.id !== action.payload.payload)))
|
||||
} catch (err) {
|
||||
yield put(actions.deleteUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>));
|
||||
}
|
||||
const effect = yield call(service.deleteUser, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
yield put(
|
||||
actions.deleteUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(
|
||||
actions.deleteUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function* sagas(): Generator {
|
||||
yield all([
|
||||
takeLatest(actions.getUsers.request, getUsers),
|
||||
takeLatest(actions.getServiceUsers.request, getServiceUsers),
|
||||
takeLatest(actions.getRegularUsers.request, getRegularUsers),
|
||||
takeLatest(actions.saveUser.request, saveUser),
|
||||
takeLatest(actions.deleteUser.request, deleteUser)
|
||||
]);
|
||||
yield all([
|
||||
takeLatest(actions.getUsers.request, getUsers),
|
||||
takeLatest(actions.getServiceUsers.request, getServiceUsers),
|
||||
takeLatest(actions.getRegularUsers.request, getRegularUsers),
|
||||
takeLatest(actions.saveUser.request, saveUser),
|
||||
takeLatest(actions.deleteUser.request, deleteUser),
|
||||
]);
|
||||
}
|
||||
|
||||
+102
-78
@@ -1,95 +1,119 @@
|
||||
import {Peer, PeerIPToID, PeerIPToName, PeerNameToIP} from "../store/peer/types";
|
||||
import {Route} from "../store/route/types";
|
||||
import {
|
||||
Peer,
|
||||
PeerIPToID,
|
||||
PeerIPToName,
|
||||
PeerNameToIP,
|
||||
} from "../store/peer/types";
|
||||
import { Route } from "../store/route/types";
|
||||
|
||||
export const routePeerSeparator = " - "
|
||||
export const routePeerSeparator = " - ";
|
||||
|
||||
export const masqueradeDisabledMSG = "Enabling this option hides other NetBird network IPs behind the routing peer local address when accessing the target Network CIDR. This option allows access to your private networks without configuring routes on your local routers or other devices."
|
||||
export const masqueradeDisabledMSG =
|
||||
"Enabling this option hides other NetBird network IPs behind the routing peer local address when accessing the target Network CIDR. This option allows access to your private networks without configuring routes on your local routers or other devices.";
|
||||
|
||||
export const masqueradeEnabledMSG = "Disabling this option stops hiding all traffic coming from other NetBird peers behind the routing peer local address when accessing the target Network CIDR. You will need to configure routes for your NetBird network pointing to your routing peer on your local routers or other devices."
|
||||
export const masqueradeEnabledMSG =
|
||||
"Disabling this option stops hiding all traffic coming from other NetBird peers behind the routing peer local address when accessing the target Network CIDR. You will need to configure routes for your NetBird network pointing to your routing peer on your local routers or other devices.";
|
||||
|
||||
export const peerToPeerIP = (name: string, ip: string): string => {
|
||||
return name + routePeerSeparator + ip
|
||||
}
|
||||
return name + routePeerSeparator + ip;
|
||||
};
|
||||
|
||||
export const initPeerMaps = (peers: Peer[]): [PeerNameToIP, PeerIPToName, PeerIPToID] => {
|
||||
let peerNameToIP = {} as PeerNameToIP
|
||||
let peerIPToName = {} as PeerIPToName
|
||||
let peerIPToID = {} as PeerIPToID
|
||||
peers.forEach((p) => {
|
||||
peerNameToIP[p.name] = p.ip
|
||||
peerIPToName[p.ip] = p.name
|
||||
peerIPToID[p.ip] = p.id ? p.id : ""
|
||||
})
|
||||
return [peerNameToIP, peerIPToName, peerIPToID]
|
||||
}
|
||||
export const initPeerMaps = (
|
||||
peers: Peer[]
|
||||
): [PeerNameToIP, PeerIPToName, PeerIPToID] => {
|
||||
let peerNameToIP = {} as PeerNameToIP;
|
||||
let peerIPToName = {} as PeerIPToName;
|
||||
let peerIPToID = {} as PeerIPToID;
|
||||
peers.forEach((p) => {
|
||||
peerNameToIP[p.name] = p.ip;
|
||||
peerIPToName[p.ip] = p.name;
|
||||
peerIPToID[p.ip] = p.id ? p.id : "";
|
||||
});
|
||||
return [peerNameToIP, peerIPToName, peerIPToID];
|
||||
};
|
||||
|
||||
export interface RouteDataTable extends Route {
|
||||
key: string;
|
||||
peer_ip: string;
|
||||
peer_name: string;
|
||||
key: string;
|
||||
peer_ip: string;
|
||||
peer_name: string;
|
||||
peer_groups: Array<string>;
|
||||
}
|
||||
|
||||
export interface GroupedDataTable {
|
||||
key: string
|
||||
network_id: any
|
||||
network: string
|
||||
enabled: boolean
|
||||
masquerade: boolean
|
||||
description: string
|
||||
routesCount: number
|
||||
groupedRoutes: RouteDataTable[]
|
||||
routesGroups: string[]
|
||||
key: string;
|
||||
network_id: any;
|
||||
network: string;
|
||||
enabled: boolean;
|
||||
masquerade: boolean;
|
||||
description: string;
|
||||
routesCount: number;
|
||||
groupedRoutes: RouteDataTable[];
|
||||
routesGroups: string[];
|
||||
peer_groups?: Array<string>;
|
||||
}
|
||||
|
||||
export const transformDataTable = (routes: Route[], peers: Peer[]): RouteDataTable[] => {
|
||||
export const transformDataTable = (
|
||||
routes: Route[],
|
||||
peers: Peer[]
|
||||
): RouteDataTable[] => {
|
||||
let peerMap = Object.fromEntries(peers.map((p) => [p.id, p]));
|
||||
return routes.map((route) => {
|
||||
return {
|
||||
key: route.id,
|
||||
...route,
|
||||
peer: route.peer,
|
||||
peer_ip: peerMap[route.peer] ? peerMap[route.peer].ip : route.peer,
|
||||
peer_name: peerMap[route.peer] ? peerMap[route.peer].name : route.peer,
|
||||
} as RouteDataTable;
|
||||
});
|
||||
};
|
||||
|
||||
let peerMap = Object.fromEntries(peers.map(p => [p.id, p]));
|
||||
return routes.map(route => {
|
||||
return {
|
||||
key: route.id,
|
||||
...route,
|
||||
peer: route.peer,
|
||||
peer_ip: peerMap[route.peer] ? peerMap[route.peer].ip : route.peer,
|
||||
peer_name: peerMap[route.peer] ? peerMap[route.peer].name : route.peer,
|
||||
} as RouteDataTable
|
||||
export const transformGroupedDataTable = (
|
||||
routes: Route[],
|
||||
peers: Peer[]
|
||||
): GroupedDataTable[] => {
|
||||
let keySet = new Set(
|
||||
routes.map((r) => {
|
||||
return r.network_id + r.network;
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
export const transformGroupedDataTable = (routes: Route[], peers: Peer[]): GroupedDataTable[] => {
|
||||
let keySet = new Set(routes.map(r => {
|
||||
return r.network_id + r.network
|
||||
}))
|
||||
let groupedRoutes: GroupedDataTable[] = [];
|
||||
|
||||
let groupedRoutes: GroupedDataTable[] = []
|
||||
|
||||
keySet.forEach((p) => {
|
||||
let hasEnabled = false
|
||||
let lastRoute: Route
|
||||
let listedRoutes: Route[] = []
|
||||
let groupList: string[] = []
|
||||
routes.forEach((r) => {
|
||||
if (p === r.network_id + r.network) {
|
||||
lastRoute = r
|
||||
if (r.enabled) {
|
||||
hasEnabled = true
|
||||
}
|
||||
listedRoutes.push(r)
|
||||
groupList = groupList.concat(r.groups)
|
||||
}
|
||||
})
|
||||
groupList = groupList.filter((value, index, arrary) => arrary.indexOf(value) === index)
|
||||
let groupDataTableRoutes = transformDataTable(listedRoutes, peers)
|
||||
groupedRoutes.push({
|
||||
key: p.toString(),
|
||||
network_id: lastRoute!.network_id,
|
||||
network: lastRoute!.network,
|
||||
masquerade: lastRoute!.masquerade,
|
||||
description: lastRoute!.description,
|
||||
enabled: hasEnabled,
|
||||
routesCount: groupDataTableRoutes.length,
|
||||
groupedRoutes: groupDataTableRoutes,
|
||||
routesGroups: groupList,
|
||||
})
|
||||
})
|
||||
return groupedRoutes
|
||||
}
|
||||
keySet.forEach((p) => {
|
||||
let hasEnabled = false;
|
||||
let lastRoute: Route;
|
||||
let listedRoutes: Route[] = [];
|
||||
let groupList: string[] = [];
|
||||
routes.forEach((r) => {
|
||||
if (p === r.network_id + r.network) {
|
||||
lastRoute = r;
|
||||
if (r.enabled) {
|
||||
hasEnabled = true;
|
||||
}
|
||||
listedRoutes.push(r);
|
||||
groupList = groupList.concat(r.groups);
|
||||
}
|
||||
});
|
||||
groupList = groupList.filter(
|
||||
(value, index, arrary) => arrary.indexOf(value) === index
|
||||
);
|
||||
let groupDataTableRoutes = transformDataTable(listedRoutes, peers);
|
||||
const filterEnabledRoutes = groupDataTableRoutes.filter(
|
||||
(route) => route.enabled
|
||||
);
|
||||
groupedRoutes.push({
|
||||
key: p.toString(),
|
||||
network_id: lastRoute!.network_id,
|
||||
network: lastRoute!.network,
|
||||
masquerade: lastRoute!.masquerade,
|
||||
description: lastRoute!.description,
|
||||
enabled: hasEnabled,
|
||||
routesCount: filterEnabledRoutes.length,
|
||||
groupedRoutes: groupDataTableRoutes,
|
||||
routesGroups: groupList,
|
||||
peer_groups: lastRoute!.peer_groups,
|
||||
});
|
||||
});
|
||||
return groupedRoutes;
|
||||
};
|
||||
+41
-50
@@ -168,37 +168,36 @@ export const Activity = () => {
|
||||
};
|
||||
const renderInitiator = (event: EventDataTable) => {
|
||||
let body = <></>;
|
||||
const user = users?.find((u) => u.id === event.initiator_id);
|
||||
if (event.initiator_id == "sys") {
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
switch (event.activity_code) {
|
||||
case "peer.login.expire":
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case "setupkey.peer.add":
|
||||
const key = setupKeys?.find((k) => k.id === event.initiator_id);
|
||||
@@ -224,7 +223,7 @@ export const Activity = () => {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (user) {
|
||||
if (event.initiator_name || event.initiator_email) {
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
@@ -235,16 +234,16 @@ export const Activity = () => {
|
||||
>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text>{user.name ? user.name : user.id}</Text>{" "}
|
||||
<Text>
|
||||
{event.initiator_name
|
||||
? event.initiator_name
|
||||
: event.initiator_id}
|
||||
</Text>{" "}
|
||||
</Row>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text type="secondary">
|
||||
{user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"}
|
||||
{event.initiator_email ? event.initiator_email : "User"}
|
||||
</Text>{" "}
|
||||
</Row>
|
||||
</span>
|
||||
@@ -281,7 +280,6 @@ export const Activity = () => {
|
||||
) {
|
||||
return "-";
|
||||
}
|
||||
const user = users?.find((u) => u.id === event.target_id);
|
||||
switch (event.activity_code) {
|
||||
case "account.create":
|
||||
case "user.join":
|
||||
@@ -334,14 +332,10 @@ export const Activity = () => {
|
||||
case "user.group.add":
|
||||
case "user.group.delete":
|
||||
case "user.role.update":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
if (event.meta.user_name) {
|
||||
@@ -365,14 +359,10 @@ export const Activity = () => {
|
||||
return renderMultiRowSpan("", "System setting");
|
||||
case "personal.access.token.create":
|
||||
case "personal.access.token.delete":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
if (event.meta.user_name) {
|
||||
@@ -387,11 +377,12 @@ export const Activity = () => {
|
||||
return renderMultiRowSpan(event.meta.name, "Service User");
|
||||
case "user.invite":
|
||||
case "user.block":
|
||||
case "user.delete":
|
||||
case "user.unblock":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email ? user.email : "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
+11
-11
@@ -248,7 +248,7 @@ export const Peers = () => {
|
||||
const t = searchText
|
||||
? searchText.toLowerCase().trim()
|
||||
: textToSearch.toLowerCase().trim();
|
||||
|
||||
|
||||
let f: Peer[] = filter(peers, (f: Peer) => {
|
||||
let userEmail: string | null;
|
||||
const u = users?.find((u) => u.id === f.user_id)?.email;
|
||||
@@ -399,11 +399,7 @@ export const Peers = () => {
|
||||
};
|
||||
|
||||
function handleSwitchSSH(record: PeerDataTable, checked: boolean) {
|
||||
const peer = {
|
||||
id: record.id,
|
||||
ssh_enabled: checked,
|
||||
name: record.name,
|
||||
} as Peer;
|
||||
const peer = { ...record, ssh_enabled: checked };
|
||||
dispatch(
|
||||
peerActions.updatePeer.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
@@ -635,16 +631,20 @@ export const Peers = () => {
|
||||
<Container style={{ paddingTop: "40px" }}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Title className="page-heading">{isAdmin ? "Peers" : "My peers"}</Title>
|
||||
<Title className="page-heading">
|
||||
{isAdmin ? "Peers" : "My peers"}
|
||||
</Title>
|
||||
{peers.length ? (
|
||||
<Paragraph style={{ marginTop: "5px" }}>
|
||||
{isAdmin ? "A list of all machines and devices connected to your private network. Use this view to manage peers" :
|
||||
"A list of all your machines and devices that you connected to NetBird."}
|
||||
{isAdmin
|
||||
? "A list of all machines and devices connected to your private network. Use this view to manage peers"
|
||||
: "A list of all your machines and devices that you connected to NetBird."}
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Paragraph style={{ marginTop: "5px" }} type={"secondary"}>
|
||||
{isAdmin ? "A list of all machines and devices connected to your private network. Use this view to manage peers" :
|
||||
"A list of all your machines and devices that you connected to NetBird."}
|
||||
{isAdmin
|
||||
? "A list of all machines and devices connected to your private network. Use this view to manage peers"
|
||||
: "A list of all your machines and devices that you connected to NetBird."}
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
|
||||
Loaded 20 of 24 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user