mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
695b571a50 | ||
|
|
ddfb6a6179 | ||
|
|
8c94119c6a |
No files matched your search
+1
-1
@@ -26,6 +26,6 @@ yarn-error.log*
|
||||
src/auth_config.json
|
||||
.idea
|
||||
.eslintcache
|
||||
src/.local-config.json
|
||||
src/.local-config*.json
|
||||
/public/OidcServiceWorker.js
|
||||
/public/OidcTrustedDomains.js
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -5,12 +5,11 @@ import { actions as routeActions } from "../store/route";
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Divider,
|
||||
Collapse,
|
||||
Form,
|
||||
Input,
|
||||
message,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
SelectProps,
|
||||
@@ -19,11 +18,6 @@ import {
|
||||
Modal,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {
|
||||
CloseOutlined,
|
||||
FlagFilled,
|
||||
QuestionCircleFilled,
|
||||
} from "@ant-design/icons";
|
||||
import CreatableSelect from "react-select/creatable";
|
||||
import { Route, RouteToSave } from "../store/route/types";
|
||||
import { Header } from "antd/es/layout/layout";
|
||||
@@ -31,7 +25,6 @@ import { RuleObject } from "antd/lib/form";
|
||||
import cidrRegex from "cidr-regex";
|
||||
import {
|
||||
initPeerMaps,
|
||||
masqueradeDisabledMSG,
|
||||
peerToPeerIP,
|
||||
routePeerSeparator,
|
||||
transformGroupedDataTable,
|
||||
@@ -55,8 +48,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
getGroupNamesFromIDs,
|
||||
selectValidator,
|
||||
} = useGetGroupTagHelpers();
|
||||
// const { optionRender, blueTagRender, grayTagRender } =
|
||||
// useGetGroupTagHelpers();
|
||||
|
||||
const { Option } = Select;
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
@@ -79,29 +71,10 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
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 [enableNetwork, setEnableNetwork] = useState(false);
|
||||
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({
|
||||
@@ -151,8 +124,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
setNewRoute(false);
|
||||
}
|
||||
|
||||
// let options = [];
|
||||
}, [route]);
|
||||
}, [route]);
|
||||
|
||||
selectedPeer &&
|
||||
selectedPeer.notPeerRoutes &&
|
||||
@@ -368,7 +340,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
};
|
||||
form.setFieldsValue(updateNetwork);
|
||||
setFormRoute(updateNetwork);
|
||||
setEnableNetwork(false)
|
||||
setEnableNetwork(false);
|
||||
} else if (!!selectedOption.__isNew__) {
|
||||
const updateNetwork = {
|
||||
...formRoute,
|
||||
@@ -390,6 +362,54 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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 && (
|
||||
@@ -416,6 +436,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
form={form}
|
||||
requiredMark={false}
|
||||
onValuesChange={onChange}
|
||||
className="route-form"
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
@@ -431,7 +452,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
fontSize: "18px",
|
||||
margin: "0px",
|
||||
fontWeight: 500,
|
||||
marginBottom: "15px",
|
||||
marginBottom: "25px",
|
||||
}}
|
||||
>
|
||||
Add Route
|
||||
@@ -573,7 +594,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
<div
|
||||
onClick={() => toggleEditDescription(true)}
|
||||
style={{
|
||||
margin: "0 0 30px",
|
||||
margin: "0 0 15px",
|
||||
lineHeight: "22px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
@@ -611,45 +632,6 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
</Row>
|
||||
</Header>
|
||||
</Col>
|
||||
{/* {!!!selectedPeer.selectedPeer && (
|
||||
<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: "500",
|
||||
}}
|
||||
>
|
||||
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={{
|
||||
@@ -804,6 +786,7 @@ const RouteAddNew = (selectedPeer: any) => {
|
||||
bordered={false}
|
||||
ghost={true}
|
||||
style={{ padding: "0" }}
|
||||
className="remove-bg"
|
||||
>
|
||||
<Panel
|
||||
key="0"
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+153
-220
File diff suppressed because it is too large.
Load diff
@@ -11,32 +11,45 @@ const {Title, Paragraph, Text} = Typography;
|
||||
export const DockerTab = () => {
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Install Docker',
|
||||
commands: (
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://docs.docker.com/engine/install/" target="_blank">Official Docker website</Button>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Run NetBird container',
|
||||
commands: formatDockerCommand(),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: "Read docs",
|
||||
commands: (
|
||||
<Link href="https://docs.netbird.io/how-to/getting-started#running-net-bird-in-docker" target="_blank">Running NetBird in Docker</Link>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand
|
||||
])
|
||||
{
|
||||
key: 1,
|
||||
title: "Install Docker",
|
||||
commands: (
|
||||
<Button
|
||||
style={{ marginTop: "5px" }}
|
||||
type="primary"
|
||||
href="https://docs.docker.com/engine/install/"
|
||||
target="_blank"
|
||||
>
|
||||
Official Docker website
|
||||
</Button>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: "Run NetBird container",
|
||||
commands: formatDockerCommand(),
|
||||
commandsForCopy: formatDockerCommand(),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: "Read docs",
|
||||
commands: (
|
||||
<Link
|
||||
href="https://docs.netbird.io/how-to/getting-started#running-net-bird-in-docker"
|
||||
target="_blank"
|
||||
>
|
||||
Running NetBird in Docker
|
||||
</Link>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div style={{marginTop: 10}}>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { useState } from 'react';
|
||||
import { useState } from "react";
|
||||
|
||||
import { Button } from "antd";
|
||||
import TabSteps from "./TabSteps";
|
||||
import { StepCommand } from "./types"
|
||||
|
||||
import { StepCommand } from "./types";
|
||||
|
||||
export const OtherTab = () => {
|
||||
const [steps, _] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: "For other installation options check our documentation.",
|
||||
commands: (
|
||||
<Button
|
||||
type="primary"
|
||||
href={`https://docs.netbird.io/how-to/getting-started#binary-install`}
|
||||
target="_blank"
|
||||
>
|
||||
Documentation
|
||||
</Button>
|
||||
),
|
||||
copied: false,
|
||||
} as StepCommand,
|
||||
]);
|
||||
|
||||
const [steps, _] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'For other installation options check our documentation.',
|
||||
commands: (
|
||||
<Button type="primary" href={`https://docs.netbird.io/how-to/getting-started#binary-install`} target="_blank">
|
||||
Documentation
|
||||
</Button>
|
||||
),
|
||||
copied: false,
|
||||
} as StepCommand,
|
||||
])
|
||||
return <TabSteps stepsItems={steps} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<TabSteps stepsItems={steps} />
|
||||
)
|
||||
}
|
||||
|
||||
export default OtherTab
|
||||
export default OtherTab;
|
||||
@@ -1,136 +1,211 @@
|
||||
import React, {useState} from 'react';
|
||||
import React, { useState } from "react";
|
||||
|
||||
import {Button, Divider, Row, Tooltip, Typography} from "antd";
|
||||
import { Button, Divider, Row, Tooltip, Typography } from "antd";
|
||||
import TabSteps from "./TabSteps";
|
||||
import {StepCommand} from "./types"
|
||||
import {formatNetBirdUP} from "./common"
|
||||
import {Collapse} from "antd";
|
||||
import { StepCommand } from "./types";
|
||||
import { formatNetBirdUP } from "./common";
|
||||
import { Collapse } from "antd";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import {QuestionCircleOutlined} from "@ant-design/icons";
|
||||
import { QuestionCircleOutlined } from "@ant-design/icons";
|
||||
import { CheckOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import { copyToClipboard } from "../../../../utils/common";
|
||||
|
||||
const {Panel} = Collapse;
|
||||
const { Panel } = Collapse;
|
||||
|
||||
const {Text} = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
export const LinuxTab = () => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [quickSteps, setQuickSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: (
|
||||
<Row>
|
||||
<Text>Download and run MacOS installer: </Text>
|
||||
<Tooltip
|
||||
title={
|
||||
<text>
|
||||
If you don't know what chip your Mac has, you can find out by
|
||||
clicking on the Apple logo in the top left corner of your screen
|
||||
and selecting 'About This Mac'. For more information click{" "}
|
||||
<a
|
||||
href="https://support.apple.com/en-us/HT211814"
|
||||
target="_blank"
|
||||
>
|
||||
here
|
||||
</a>
|
||||
</text>
|
||||
}
|
||||
className={"ant-form-item-tooltip"}
|
||||
>
|
||||
<QuestionCircleOutlined
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.45)",
|
||||
cursor: "help",
|
||||
marginLeft: "3px",
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Row>
|
||||
),
|
||||
commands: (
|
||||
<Row style={{ paddingTop: "5px" }}>
|
||||
<Button
|
||||
style={{ marginRight: "10px" }}
|
||||
type="primary"
|
||||
href="https://pkgs.netbird.io/macos/amd64"
|
||||
>
|
||||
Download for Intel
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginRight: "10px" }}
|
||||
type="default"
|
||||
href="https://pkgs.netbird.io/macos/arm64"
|
||||
>
|
||||
Download for M1 & M2
|
||||
</Button>
|
||||
</Row>
|
||||
),
|
||||
copied: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Click on "Connect" from the NetBird icon in your system tray',
|
||||
commands: "",
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
title: "Sign up using your email address",
|
||||
commands: "",
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const [quickSteps, setQuickSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: (
|
||||
<Row>
|
||||
<Text>Download and run MacOS installer: </Text>
|
||||
<Tooltip title={
|
||||
<text>If you don't know what chip your Mac has, you can find out by clicking on the Apple logo
|
||||
in the top left corner of your screen and selecting 'About This Mac'. For more information
|
||||
click <a href="https://support.apple.com/en-us/HT211814" target="_blank">here</a></text>
|
||||
}
|
||||
className={"ant-form-item-tooltip"}>
|
||||
<QuestionCircleOutlined
|
||||
style={{color: "rgba(0, 0, 0, 0.45)", cursor: "help", marginLeft: "3px"}}/>
|
||||
</Tooltip>
|
||||
</Row>
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: "Download and install Homebrew",
|
||||
commands: (
|
||||
<Button
|
||||
style={{ marginTop: "5px" }}
|
||||
type="primary"
|
||||
href="https://brew.sh/"
|
||||
target="_blank"
|
||||
>
|
||||
Download Brew
|
||||
</Button>
|
||||
),
|
||||
copied: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: "Install NetBird:",
|
||||
commands: [
|
||||
`# for CLI only`,
|
||||
`brew install netbirdio/tap/netbird`,
|
||||
`# for GUI package`,
|
||||
`brew install --cask netbirdio/tap/netbird-ui`,
|
||||
].join("\n"),
|
||||
commandsForCopy: [
|
||||
`brew install netbirdio/tap/netbird`,
|
||||
`brew install --cask netbirdio/tap/netbird-ui`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: true,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: "Start NetBird daemon:",
|
||||
commands: [
|
||||
`sudo netbird service install`,
|
||||
`sudo netbird service start`,
|
||||
].join("\n"),
|
||||
commandsForCopy: [
|
||||
`sudo netbird service install`,
|
||||
`sudo netbird service start`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: true,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 4,
|
||||
title: "Run NetBird and log in the browser:",
|
||||
commands: formatNetBirdUP(),
|
||||
commandsForCopy: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: true,
|
||||
} as StepCommand,
|
||||
]);
|
||||
|
||||
),
|
||||
commands: (
|
||||
<Row style={{paddingTop: "5px"}}>
|
||||
<Button style={{marginRight: "10px"}} type="primary" href="https://pkgs.netbird.io/macos/amd64">Download
|
||||
for Intel</Button>
|
||||
<Button style={{marginRight: "10px"}} type="default" href="https://pkgs.netbird.io/macos/arm64">Download
|
||||
for M1 & M2</Button>
|
||||
</Row>
|
||||
),
|
||||
copied: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Click on "Connect" from the NetBird icon in your system tray',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
title: 'Sign up using your email address',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
}
|
||||
])
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Download and install Homebrew',
|
||||
commands: (
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://brew.sh/" target="_blank">Download
|
||||
Brew</Button>
|
||||
),
|
||||
copied: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Install NetBird:',
|
||||
commands: [
|
||||
`# for CLI only`,
|
||||
`brew install netbirdio/tap/netbird`,
|
||||
`# for GUI package`,
|
||||
`brew install --cask netbirdio/tap/netbird-ui`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: 'Start NetBird daemon:',
|
||||
commands: [
|
||||
`sudo netbird service install`,
|
||||
`sudo netbird service start`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 4,
|
||||
title: 'Run NetBird and log in the browser:',
|
||||
commands: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand
|
||||
])
|
||||
return (
|
||||
<div style={{marginTop: 10}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on MacOS
|
||||
</Text>
|
||||
<div style={{marginTop: 5, marginBottom: 5}}>
|
||||
<TabSteps stepsItems={quickSteps}/>
|
||||
const onCopyClick = () => {
|
||||
const stringToCopy = "curl -fsSL https://pkgs.netbird.io/install.sh | sh";
|
||||
copyToClipboard(stringToCopy);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Text style={{ fontWeight: "bold" }}>Install on MacOS</Text>
|
||||
<div style={{ marginTop: 5, marginBottom: 5 }}>
|
||||
<TabSteps stepsItems={quickSteps} />
|
||||
</div>
|
||||
<div style={{ marginTop: 0 }} />
|
||||
{/*<Divider style={{marginTop: "5px"}} />*/}
|
||||
<Collapse bordered={false} style={{ backgroundColor: "unset" }}>
|
||||
<Panel
|
||||
className="CustomPopupCollapse"
|
||||
header={<Text strong={true}>Or install via command line</Text>}
|
||||
key="1"
|
||||
>
|
||||
<div style={{ marginLeft: "25px" }}>
|
||||
<Text style={{ fontWeight: "bold" }}>Install with one command</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: ".85em",
|
||||
marginTop: 5,
|
||||
marginBottom: 25,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{!copied ? (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CopyOutlined />}
|
||||
style={{ color: "rgb(107, 114, 128)", top: "0", zIndex: "3" }}
|
||||
onClick={onCopyClick}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CheckOutlined />}
|
||||
style={{ color: "green", top: "0", zIndex: "3" }}
|
||||
/>
|
||||
)}
|
||||
<SyntaxHighlighter language="bash">
|
||||
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
<div style={{marginTop: 0}}/>
|
||||
{/*<Divider style={{marginTop: "5px"}} />*/}
|
||||
<Collapse bordered={false} style={{backgroundColor: "unset"}}>
|
||||
<Panel className="CustomPopupCollapse" header={<Text strong={true}>Or install via command line</Text>}
|
||||
key="1">
|
||||
<div style={{marginLeft: "25px"}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install with one command
|
||||
</Text>
|
||||
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
|
||||
<SyntaxHighlighter language="bash">
|
||||
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Or install manually with HomeBrew
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<Text style={{ fontWeight: "bold" }}>
|
||||
Or install manually with HomeBrew
|
||||
</Text>
|
||||
<div style={{ marginTop: 5 }}>
|
||||
<TabSteps stepsItems={steps} />
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinuxTab
|
||||
export default LinuxTab;
|
||||
@@ -1,63 +1,89 @@
|
||||
import "highlight.js/styles/mono-blue.css";
|
||||
import "highlight.js/lib/languages/bash";
|
||||
import { StepCommand } from './types'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import {
|
||||
Typography,
|
||||
Space,
|
||||
Steps, Button, Popover, StepsProps
|
||||
} from "antd";
|
||||
import {copyToClipboard} from "../../../../utils/common";
|
||||
import {CheckOutlined, CopyOutlined} from "@ant-design/icons";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import { StepCommand } from "./types";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { Typography, Space, Steps, Button, Popover, StepsProps } from "antd";
|
||||
import { copyToClipboard } from "../../../../utils/common";
|
||||
import { CheckOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import React, { useEffect, useState } from "react";
|
||||
const { Step } = Steps;
|
||||
const {Text} = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
type Props = {
|
||||
stepsItems: Array<StepCommand>
|
||||
stepsItems: Array<StepCommand>;
|
||||
};
|
||||
|
||||
const TabSteps:React.FC<Props> = ({stepsItems}) => {
|
||||
const TabSteps: React.FC<Props> = ({ stepsItems }) => {
|
||||
const [steps, setSteps] = useState(stepsItems);
|
||||
|
||||
const [steps, setSteps] = useState(stepsItems)
|
||||
useEffect(() => setSteps(stepsItems), [stepsItems]);
|
||||
|
||||
useEffect(() => setSteps(stepsItems), [stepsItems])
|
||||
const onCopyClick = (
|
||||
key: string | number,
|
||||
commands: React.ReactNode | string,
|
||||
copied: boolean
|
||||
) => {
|
||||
if (!(typeof commands === "string")) return;
|
||||
copyToClipboard(commands);
|
||||
const step = steps.find((s) => s.key === key);
|
||||
if (step) step.copied = copied;
|
||||
setSteps([...steps]);
|
||||
|
||||
const onCopyClick = (key: string | number, commands:React.ReactNode | string, copied: boolean) => {
|
||||
if (!(typeof commands === 'string')) return
|
||||
copyToClipboard(commands)
|
||||
const step = steps.find(s => s.key === key)
|
||||
if (step) step.copied = copied
|
||||
setSteps([...steps])
|
||||
|
||||
if (copied) {
|
||||
setTimeout(() => {
|
||||
onCopyClick(key, commands, false)
|
||||
}, 2000)
|
||||
}
|
||||
if (copied) {
|
||||
setTimeout(() => {
|
||||
onCopyClick(key, commands, false);
|
||||
}, 2000);
|
||||
}
|
||||
return (
|
||||
<Steps direction="vertical" size={"small"}>
|
||||
{steps.map(c =>
|
||||
<Step
|
||||
status={"process"}
|
||||
key={c.key}
|
||||
title={<Text>{c.title}</Text>}
|
||||
description={
|
||||
<Space className="nb-code" direction="vertical" size="small" style={{display: "flex", fontSize: ".85em"}}>
|
||||
{ (c.commands && (typeof c.commands === 'string')) ? (
|
||||
<SyntaxHighlighter language="bash">
|
||||
{c.commands}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
c.commands
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Steps>
|
||||
)
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Steps direction="vertical" size={"small"}>
|
||||
{steps.map((c) => (
|
||||
<Step
|
||||
status={"process"}
|
||||
key={c.key}
|
||||
title={<Text>{c.title}</Text>}
|
||||
description={
|
||||
<Space
|
||||
className="nb-code"
|
||||
direction="vertical"
|
||||
size="small"
|
||||
style={{ display: "flex", fontSize: ".85em" }}
|
||||
>
|
||||
{c.commands && typeof c.commands === "string" ? (
|
||||
<>
|
||||
{!c.copied ? (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CopyOutlined />}
|
||||
style={{ color: "rgb(107, 114, 128)"}}
|
||||
onClick={() => {
|
||||
onCopyClick(c.key, c.commandsForCopy, true);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CheckOutlined />}
|
||||
style={{ color: "green"}}
|
||||
/>
|
||||
)}
|
||||
<SyntaxHighlighter language="bash">
|
||||
{c.commands}
|
||||
</SyntaxHighlighter>
|
||||
</>
|
||||
) : (
|
||||
c.commands
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Steps>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabSteps;
|
||||
export default TabSteps;
|
||||
@@ -1,68 +1,110 @@
|
||||
import React, {useState} from 'react';
|
||||
import {StepCommand} from "./types"
|
||||
import {formatNetBirdUP} from "./common"
|
||||
import React, { useState } from "react";
|
||||
import { StepCommand } from "./types";
|
||||
import { formatNetBirdUP } from "./common";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import TabSteps from "./TabSteps";
|
||||
import {Typography} from "antd";
|
||||
import { Typography, Button } from "antd";
|
||||
import { CheckOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import { copyToClipboard } from "../../../../utils/common";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
const { Title, Paragraph, Text } = Typography;
|
||||
|
||||
export const UbuntuTab = () => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Add repository',
|
||||
commands: [
|
||||
`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`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Install NetBird',
|
||||
commands: [
|
||||
`sudo apt-get update`,
|
||||
`# for CLI only`,
|
||||
`sudo apt-get install netbird`,
|
||||
`# for GUI package`,
|
||||
`sudo apt-get install netbird-ui`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: 'Run NetBird and log in the browser',
|
||||
commands: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand
|
||||
])
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: "Add repository",
|
||||
commands: [
|
||||
`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`,
|
||||
].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`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: "Install NetBird",
|
||||
commands: [
|
||||
`sudo apt-get update`,
|
||||
`# for CLI only`,
|
||||
`sudo apt-get install netbird`,
|
||||
`# for GUI package`,
|
||||
`sudo apt-get install netbird-ui`,
|
||||
].join("\n"),
|
||||
commandsForCopy: [
|
||||
`sudo apt-get update`,
|
||||
`sudo apt-get install netbird`,
|
||||
`sudo apt-get install netbird-ui`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: "Run NetBird and log in the browser",
|
||||
commands: formatNetBirdUP(),
|
||||
commandsForCopy: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
} as StepCommand,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div style={{marginTop: 10}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install with one command
|
||||
</Text>
|
||||
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
|
||||
<SyntaxHighlighter language="bash">
|
||||
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Or install manually on Ubuntu
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
const onCopyClick = () => {
|
||||
const stringToCopy = "curl -fsSL https://pkgs.netbird.io/install.sh | sh";
|
||||
copyToClipboard(stringToCopy);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
</div>
|
||||
return (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Text style={{ fontWeight: "bold" }}>Install with one command</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: ".85em",
|
||||
marginTop: 5,
|
||||
marginBottom: 25,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{!copied ? (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CopyOutlined />}
|
||||
style={{ color: "rgb(107, 114, 128)", top: "0", zIndex: "3" }}
|
||||
onClick={onCopyClick}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
className="btn-copy-code peer"
|
||||
icon={<CheckOutlined />}
|
||||
style={{ color: "green", top: "0", zIndex: "3" }}
|
||||
/>
|
||||
)}
|
||||
<SyntaxHighlighter language="bash">
|
||||
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
<Text style={{ fontWeight: "bold" }}>Or install manually on Ubuntu</Text>
|
||||
<div style={{ marginTop: 5 }}>
|
||||
<TabSteps stepsItems={steps} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default UbuntuTab
|
||||
export default UbuntuTab;
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as React from "react";
|
||||
|
||||
export interface StepCommand {
|
||||
key: number | string,
|
||||
title: React.ReactNode | string | null,
|
||||
commands: React.ReactNode | string | null,
|
||||
copied?: boolean,
|
||||
showCopyButton?: boolean
|
||||
key: number | string;
|
||||
title: React.ReactNode | string | null;
|
||||
commands: React.ReactNode | string | null;
|
||||
commandsForCopy?: React.ReactNode | string | null;
|
||||
copied?: boolean;
|
||||
showCopyButton?: boolean;
|
||||
}
|
||||
+156
-3
@@ -19,7 +19,6 @@ body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
min-height: 64px;
|
||||
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
@@ -191,6 +190,10 @@ td.non-highlighted-table-column {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ant-modal-content {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
@@ -269,6 +272,156 @@ td.non-highlighted-table-column {
|
||||
}
|
||||
|
||||
.ant-badge-status-dot {
|
||||
width: 8px!important;
|
||||
height: 8px!important;
|
||||
width: 8px !important;
|
||||
height: 8px !important;
|
||||
}
|
||||
|
||||
.routes-accordian {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.routes-accordian .ant-collapse-header {
|
||||
background: #fff;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.ant-collapse-content-box {
|
||||
background: #FAFAFA;
|
||||
padding: 5px 16px !important;
|
||||
}
|
||||
|
||||
.accordian-header p {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin: 0;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.headerInner {
|
||||
display: flex;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.headerInner p {
|
||||
margin: 0;
|
||||
width: 33.33%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.accordian-header {
|
||||
display: flex;
|
||||
padding: 10px 40px;
|
||||
}
|
||||
|
||||
.accordian-inner-header {
|
||||
display: flex;
|
||||
padding: 10px 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.accordian-inner-header p {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin: 0;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.accordian-inner-listing {
|
||||
padding: 3px 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.accordian-inner-listing p {
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
width: 10%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.headerInner .text-right {
|
||||
display: block;
|
||||
text-align: right;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.accordian-inner-listing p:nth-child(5) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.accordian-inner-listing p:nth-child(1) {
|
||||
width: 25%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.accordian-inner-header p:nth-child(1) {
|
||||
width: calc(20% + 50px);
|
||||
}
|
||||
|
||||
.accordian-inner-listing p:nth-child(2) {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.accordian-inner-header p:nth-child(2) {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.accordian-inner-listing p:nth-child(3) {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.accordian-inner-listing p:nth-child(4) {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.ant-list-item {
|
||||
padding: 5px 0 !important;
|
||||
margin-inline: 0 !important;
|
||||
}
|
||||
|
||||
ul.ant-list-items {
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
|
||||
.container-spinner {
|
||||
margin: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
padding: 30px 50px;
|
||||
text-align: center;
|
||||
background: rgb(0 0 0 / 2%);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.remove-bg .ant-collapse-content-box {
|
||||
background-color: transparent;
|
||||
}
|
||||
.route-form .ant-form-item {
|
||||
margin-bottom: 20px;
|
||||
/* width: 8px!important;
|
||||
height: 8px!important; */
|
||||
}
|
||||
.nb-code{
|
||||
position: relative;
|
||||
}
|
||||
.btn-copy-code.peer {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 2px;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Method } from 'axios';
|
||||
import { Method } from "axios";
|
||||
|
||||
export interface RequestPayload<T> {
|
||||
getAccessTokenSilently: any | null;
|
||||
queryParams?: any | null;
|
||||
payload:T;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
export interface RequestHeader {
|
||||
'content-type': string;
|
||||
"content-type": string;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ export interface RequestConfig {
|
||||
export interface ApiRequestParams extends RequestConfig {
|
||||
method: Method;
|
||||
url: string;
|
||||
params?: any,
|
||||
params?: any;
|
||||
data: unknown;
|
||||
urlBase: string;
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
code:string;
|
||||
message:string;
|
||||
data?:any;
|
||||
statusCode:number;
|
||||
code: string;
|
||||
message: string;
|
||||
data?: any;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export interface DeleteResponse<T> {
|
||||
@@ -45,7 +45,7 @@ export interface DeleteResponse<T> {
|
||||
success: boolean;
|
||||
failure: boolean;
|
||||
error: ApiError | null;
|
||||
data : T;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface CreateResponse<T> {
|
||||
@@ -53,7 +53,7 @@ export interface CreateResponse<T> {
|
||||
success: boolean;
|
||||
failure: boolean;
|
||||
error: ApiError | null;
|
||||
data : T;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface ChangeResponse<T> {
|
||||
@@ -61,5 +61,5 @@ export interface ChangeResponse<T> {
|
||||
success: boolean;
|
||||
failure: boolean;
|
||||
error: ApiError | null;
|
||||
data : T;
|
||||
}
|
||||
data: T;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ const actions = {
|
||||
'DELETE_ROUTE_REQUEST',
|
||||
'DELETE_ROUTE_SUCCESS',
|
||||
'DELETE_ROUTE_FAILURE'
|
||||
)<RequestPayload<string>, DeleteResponse<string | null>, DeleteResponse<string | null>>(),
|
||||
)<RequestPayload<any>, DeleteResponse<string | null>, DeleteResponse<string | null>>(),
|
||||
setDeletedRoute: createAction('SET_DELETED_ROUTE')<DeleteResponse<string | null>>(),
|
||||
resetDeletedRoute: createAction('RESET_DELETED_ROUTE')<null>(),
|
||||
removeRoute: createAction('REMOVE_ROUTE')<string>(),
|
||||
@@ -29,6 +29,7 @@ const actions = {
|
||||
setRoute: createAction('SET_ROUTE')<Route>(),
|
||||
setSetupNewRouteVisible: createAction('SET_SETUP_NEW_ROUTE_VISIBLE')<boolean>(),
|
||||
setSetupEditRouteVisible: createAction('SET_SETUP_EDIT_ROUTE_VISIBLE')<boolean>(),
|
||||
setSetupEditRoutePeerVisible: createAction('SET_SETUP_EDIT_ROUTE_PEER_VISIBLE')<boolean>(),
|
||||
setSetupNewRouteHA: createAction('SET_SETUP_NEW_ROUTE_HA')<boolean>()
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ type StateType = Readonly<{
|
||||
setupNewRouteVisible: boolean;
|
||||
setupNewRouteHA: boolean;
|
||||
setupEditRouteVisible: boolean;
|
||||
setEditRoutePeerVisible: boolean;
|
||||
}>;
|
||||
|
||||
const initialState: StateType = {
|
||||
@@ -40,6 +41,7 @@ const initialState: StateType = {
|
||||
setupNewRouteVisible: false,
|
||||
setupNewRouteHA: false,
|
||||
setupEditRouteVisible: false,
|
||||
setEditRoutePeerVisible: false,
|
||||
};
|
||||
|
||||
const data = createReducer<Route[], ActionTypes>(initialState.data as Route[])
|
||||
@@ -88,6 +90,14 @@ const setupEditRouteVisible = createReducer<boolean, ActionTypes>(
|
||||
(store, action) => action.payload
|
||||
);
|
||||
|
||||
|
||||
const setEditRoutePeerVisible = createReducer<boolean, ActionTypes>(
|
||||
initialState.setEditRoutePeerVisible
|
||||
).handleAction(
|
||||
actions.setSetupEditRoutePeerVisible,
|
||||
(store, action) => action.payload
|
||||
);
|
||||
|
||||
const setupNewRouteHA = createReducer<boolean, ActionTypes>(initialState.setupNewRouteHA)
|
||||
.handleAction(actions.setSetupNewRouteHA, (store, action) => action.payload)
|
||||
|
||||
@@ -102,4 +112,5 @@ export default combineReducers({
|
||||
setupNewRouteVisible,
|
||||
setupNewRouteHA,
|
||||
setupEditRouteVisible,
|
||||
setEditRoutePeerVisible,
|
||||
});
|
||||
+171
-94
@@ -1,22 +1,30 @@
|
||||
import {all, call, put, select, takeLatest} from 'redux-saga/effects';
|
||||
import {ApiError, ApiResponse, CreateResponse, DeleteResponse} from '../../services/api-client/types';
|
||||
import {Route} 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 { Route } from "./types";
|
||||
import service from "./service";
|
||||
import actions from "./actions";
|
||||
import serviceGroup from "../group/service";
|
||||
import {Group} from "../group/types";
|
||||
import {actions as groupActions} from "../group";
|
||||
import { Group } from "../group/types";
|
||||
import { actions as groupActions } from "../group";
|
||||
|
||||
export function* getRoutes(action: ReturnType<typeof actions.getRoutes.request>): Generator {
|
||||
export function* getRoutes(
|
||||
action: ReturnType<typeof actions.getRoutes.request>
|
||||
): Generator {
|
||||
try {
|
||||
|
||||
yield put(actions.setDeletedRoute({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>))
|
||||
yield put(
|
||||
actions.setDeletedRoute({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
|
||||
const effect = yield call(service.getRoutes, action.payload);
|
||||
const response = effect as ApiResponse<Route[]>;
|
||||
@@ -27,36 +35,48 @@ export function* getRoutes(action: ReturnType<typeof actions.getRoutes.request>)
|
||||
}
|
||||
}
|
||||
|
||||
export function* setCreatedRoute(action: ReturnType<typeof actions.setSavedRoute>): Generator {
|
||||
yield put(actions.setSavedRoute(action.payload))
|
||||
export function* setCreatedRoute(
|
||||
action: ReturnType<typeof actions.setSavedRoute>
|
||||
): Generator {
|
||||
yield put(actions.setSavedRoute(action.payload));
|
||||
}
|
||||
|
||||
export function* saveRoute(action: ReturnType<typeof actions.saveRoute.request>): Generator {
|
||||
export function* saveRoute(
|
||||
action: ReturnType<typeof actions.saveRoute.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield put(actions.setSavedRoute({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as CreateResponse<Route | null>))
|
||||
yield put(
|
||||
actions.setSavedRoute({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as CreateResponse<Route | null>)
|
||||
);
|
||||
|
||||
const routeToSave = action.payload.payload
|
||||
const routeToSave = action.payload.payload;
|
||||
|
||||
let groupsToCreate = routeToSave.groupsToCreate
|
||||
let groupsToCreate = routeToSave.groupsToCreate;
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = []
|
||||
groupsToCreate = [];
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(groupsToCreate.map(g => call(serviceGroup.createGroup, {
|
||||
const responsesGroup = yield all(
|
||||
groupsToCreate.map((g) =>
|
||||
call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: {name: g}
|
||||
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 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 payloadToSave = {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
@@ -69,86 +89,144 @@ export function* saveRoute(action: ReturnType<typeof actions.saveRoute.request>)
|
||||
network: routeToSave.network,
|
||||
network_id: routeToSave.network_id,
|
||||
peer: routeToSave.peer,
|
||||
groups: newGroups
|
||||
} as Route
|
||||
}
|
||||
groups: newGroups,
|
||||
} as Route,
|
||||
};
|
||||
|
||||
let effect
|
||||
let effect;
|
||||
if (!routeToSave.id) {
|
||||
effect = yield call(service.createRoute, payloadToSave);
|
||||
effect = yield call(service.createRoute, payloadToSave);
|
||||
} else {
|
||||
payloadToSave.payload.id = routeToSave.id
|
||||
payloadToSave.payload.id = routeToSave.id;
|
||||
effect = yield call(service.editRoute, payloadToSave);
|
||||
}
|
||||
|
||||
const response = effect as ApiResponse<Route>;
|
||||
|
||||
yield put(actions.saveRoute.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as CreateResponse<Route | null>));
|
||||
yield put(
|
||||
actions.saveRoute.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as CreateResponse<Route | null>)
|
||||
);
|
||||
|
||||
yield put(groupActions.getGroups.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null
|
||||
}));
|
||||
|
||||
yield put(actions.getRoutes.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null }));
|
||||
yield put(
|
||||
groupActions.getGroups.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
|
||||
yield put(
|
||||
actions.getRoutes.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(groupActions.getGroups.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null
|
||||
}));
|
||||
yield put(
|
||||
groupActions.getGroups.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
|
||||
yield put(actions.saveRoute.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: true,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as CreateResponse<Route | null>));
|
||||
yield put(
|
||||
actions.saveRoute.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: true,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as CreateResponse<Route | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* setDeleteRoute(action: ReturnType<typeof actions.setDeletedRoute>): Generator {
|
||||
yield put(actions.setDeletedRoute(action.payload))
|
||||
export function* setDeleteRoute(
|
||||
action: ReturnType<typeof actions.setDeletedRoute>
|
||||
): Generator {
|
||||
yield put(actions.setDeletedRoute(action.payload));
|
||||
}
|
||||
|
||||
export function* deleteRoute(action: ReturnType<typeof actions.deleteRoute.request>): Generator {
|
||||
export function* deleteRoute(
|
||||
action: ReturnType<typeof actions.deleteRoute.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield call(actions.setDeletedRoute,{
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>)
|
||||
yield put(
|
||||
actions.setDeletedRoute({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
|
||||
const effect = yield call(service.deletedRoute, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
const payloadType = typeof action.payload.payload;
|
||||
|
||||
yield put(actions.deleteRoute.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as DeleteResponse<string | null>));
|
||||
if (payloadType === "string") {
|
||||
const effect = yield call(service.deletedRoute, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
|
||||
const routes = (yield select(state => state.route.data)) as Route[]
|
||||
yield put(actions.getRoutes.success(routes.filter((p:Route) => p.id !== action.payload.payload)))
|
||||
yield put(
|
||||
actions.deleteRoute.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
const routes = (yield select((state) => state.route.data)) as Route[];
|
||||
|
||||
yield put(
|
||||
actions.getRoutes.success(
|
||||
routes.filter((p: Route) => p.id !== action.payload.payload)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const effect = yield all(
|
||||
action.payload.payload.map((g: string) =>
|
||||
call(service.deletedRoute, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: g,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const response = effect as Array<ApiResponse<any>>;
|
||||
yield put(
|
||||
actions.deleteRoute.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response[0].body,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
|
||||
const routes = (yield select((state) => state.route.data)) as Route[];
|
||||
|
||||
yield put(
|
||||
actions.getRoutes.success(
|
||||
routes.filter((p: Route) => !action.payload.payload.includes(p.id))
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
yield put(actions.deleteRoute.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>));
|
||||
yield put(
|
||||
actions.deleteRoute.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +234,6 @@ export default function* sagas(): Generator {
|
||||
yield all([
|
||||
takeLatest(actions.getRoutes.request, getRoutes),
|
||||
takeLatest(actions.saveRoute.request, saveRoute),
|
||||
takeLatest(actions.deleteRoute.request, deleteRoute)
|
||||
takeLatest(actions.deleteRoute.request, deleteRoute),
|
||||
]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ export interface RouteDataTable extends Route {
|
||||
|
||||
export interface GroupedDataTable {
|
||||
key: string
|
||||
network_id: string
|
||||
network_id: any
|
||||
network: string
|
||||
enabled: boolean
|
||||
masquerade: boolean
|
||||
|
||||
+234
-218
@@ -111,7 +111,8 @@ export const AccessControl = () => {
|
||||
|
||||
const isShowTutorial = (policy: Policy[]): boolean => {
|
||||
return (
|
||||
!policy.length || (policy.length === 1 && policy[0].name === "Default")
|
||||
// !policy.length || (policy.length === 1 && policy[0].name === "Default")
|
||||
!policy.length
|
||||
);
|
||||
};
|
||||
|
||||
@@ -541,6 +542,7 @@ export const AccessControl = () => {
|
||||
})
|
||||
);
|
||||
};
|
||||
console.log("dataTable", dataTable);
|
||||
return (
|
||||
<>
|
||||
{!setupEditPolicyVisible && (
|
||||
@@ -550,24 +552,26 @@ export const AccessControl = () => {
|
||||
<Col span={24}>
|
||||
<Title className="page-heading">Access Control</Title>
|
||||
{policies.length ? (
|
||||
<Paragraph style={{marginTop: "5px"}}>
|
||||
Create rules to manage access in your network and define what peers can connect.
|
||||
<Paragraph style={{ marginTop: "5px" }}>
|
||||
Create rules to manage access in your network and define
|
||||
what peers can connect.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Paragraph style={{marginTop: "5px"}} type={"secondary"}>
|
||||
Create rules to manage access in your network and define what peers can connect.
|
||||
<Paragraph style={{ marginTop: "5px" }} type={"secondary"}>
|
||||
Create rules to manage access in your network and define
|
||||
what peers can connect.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
@@ -617,19 +621,29 @@ export const AccessControl = () => {
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
{!showTutorial && (<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={savedPolicy.loading}
|
||||
onClick={onClickAddNewPolicy}
|
||||
>
|
||||
Add Rule
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>)}
|
||||
{!showTutorial && (
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={5}
|
||||
lg={5}
|
||||
xl={5}
|
||||
xxl={5}
|
||||
span={5}
|
||||
>
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={savedPolicy.loading}
|
||||
onClick={onClickAddNewPolicy}
|
||||
>
|
||||
Add Rule
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
{failed && (
|
||||
<Alert
|
||||
@@ -641,207 +655,209 @@ export const AccessControl = () => {
|
||||
/>
|
||||
)}
|
||||
<Card bodyStyle={{ padding: 0 }}>
|
||||
{!showTutorial && (
|
||||
<Table
|
||||
pagination={{
|
||||
current: currentPage,
|
||||
hideOnSinglePage: showTutorial,
|
||||
disabled: showTutorial,
|
||||
pageSize,
|
||||
responsive: true,
|
||||
showSizeChanger: false,
|
||||
showTotal: (total, range) =>
|
||||
`Showing ${range[0]} to ${range[1]} of ${total} rules`,
|
||||
onChange: (page, pageSize) => {
|
||||
setCurrentPage(page);
|
||||
},
|
||||
}}
|
||||
className={`access-control-table ${
|
||||
showTutorial
|
||||
? "card-table card-table-no-placeholder"
|
||||
: "card-table"
|
||||
}`}
|
||||
showSorterTooltip={false}
|
||||
scroll={{ x: true }}
|
||||
loading={tableSpin(loading)}
|
||||
dataSource={dataTable}
|
||||
>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
onFilter={(value: string | number | boolean, record) =>
|
||||
(record as any).name.includes(value)
|
||||
}
|
||||
sorter={(a, b) =>
|
||||
(a as any).name.localeCompare((b as any).name)
|
||||
}
|
||||
defaultSortOrder="ascend"
|
||||
render={(text, record, index) => {
|
||||
const desc = (
|
||||
record as PolicyDataTable
|
||||
).description.trim();
|
||||
return (
|
||||
<Tooltip
|
||||
title={desc !== "" ? desc : "no description"}
|
||||
arrowPointAtCenter
|
||||
>
|
||||
<span
|
||||
onClick={() =>
|
||||
setPolicyAndView(record as PolicyDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
<Text className="font-500">{text}</Text>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
{!showTutorial && (
|
||||
<Table
|
||||
pagination={{
|
||||
current: currentPage,
|
||||
hideOnSinglePage: showTutorial,
|
||||
disabled: showTutorial,
|
||||
pageSize,
|
||||
responsive: true,
|
||||
showSizeChanger: false,
|
||||
showTotal: (total, range) =>
|
||||
`Showing ${range[0]} to ${range[1]} of ${total} rules`,
|
||||
onChange: (page, pageSize) => {
|
||||
setCurrentPage(page);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Enabled"
|
||||
dataIndex="enabled"
|
||||
render={(
|
||||
text: Boolean,
|
||||
record: PolicyDataTable,
|
||||
index
|
||||
) => {
|
||||
return (
|
||||
<Switch
|
||||
size={"small"}
|
||||
checked={record.enabled}
|
||||
onChange={(isOpen: boolean) => {
|
||||
onEnableChange(isOpen, record);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Sources"
|
||||
dataIndex="sourceLabel"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPopoverGroups(
|
||||
text,
|
||||
record.sources,
|
||||
record as PolicyDataTable
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Direction"
|
||||
dataIndex="bidirectional"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
const s = {
|
||||
minWidth: 62,
|
||||
textAlign: "center",
|
||||
} as React.CSSProperties;
|
||||
if (record.bidirectional) {
|
||||
className={`access-control-table ${
|
||||
showTutorial
|
||||
? "card-table card-table-no-placeholder"
|
||||
: "card-table"
|
||||
}`}
|
||||
showSorterTooltip={false}
|
||||
scroll={{ x: true }}
|
||||
loading={tableSpin(loading)}
|
||||
dataSource={dataTable}
|
||||
>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
onFilter={(
|
||||
value: string | number | boolean,
|
||||
record
|
||||
) => (record as any).name.includes(value)}
|
||||
sorter={(a, b) =>
|
||||
(a as any).name.localeCompare((b as any).name)
|
||||
}
|
||||
defaultSortOrder="ascend"
|
||||
render={(text, record, index) => {
|
||||
const desc = (
|
||||
record as PolicyDataTable
|
||||
).description.trim();
|
||||
return (
|
||||
<Tag color="green" style={s}>
|
||||
<img src={bidirect} alt="bi icon" />
|
||||
<Tooltip
|
||||
title={desc !== "" ? desc : "no description"}
|
||||
arrowPointAtCenter
|
||||
>
|
||||
<span
|
||||
onClick={() =>
|
||||
setPolicyAndView(record as PolicyDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
<Text className="font-500">{text}</Text>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Enabled"
|
||||
dataIndex="enabled"
|
||||
render={(
|
||||
text: Boolean,
|
||||
record: PolicyDataTable,
|
||||
index
|
||||
) => {
|
||||
return (
|
||||
<Switch
|
||||
size={"small"}
|
||||
checked={record.enabled}
|
||||
onChange={(isOpen: boolean) => {
|
||||
onEnableChange(isOpen, record);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Sources"
|
||||
dataIndex="sourceLabel"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPopoverGroups(
|
||||
text,
|
||||
record.sources,
|
||||
record as PolicyDataTable
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Direction"
|
||||
dataIndex="bidirectional"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
const s = {
|
||||
minWidth: 62,
|
||||
textAlign: "center",
|
||||
} as React.CSSProperties;
|
||||
if (record.bidirectional) {
|
||||
return (
|
||||
<Tag color="green" style={s}>
|
||||
<img src={bidirect} alt="bi icon" />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tag color="processing" style={s}>
|
||||
<img src={outbound} alt="out icon" />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tag color="processing" style={s}>
|
||||
<img src={outbound} alt="out icon" />
|
||||
</Tag>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Destinations"
|
||||
dataIndex="destinationLabel"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPopoverGroups(
|
||||
text,
|
||||
record.destinations,
|
||||
record as PolicyDataTable
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Protocol"
|
||||
dataIndex="protocol"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return (
|
||||
<Tag
|
||||
className="menlo-font"
|
||||
style={{
|
||||
marginRight: "3",
|
||||
textTransform: "uppercase",
|
||||
}}
|
||||
>
|
||||
{record.protocol}
|
||||
</Tag>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Ports"
|
||||
dataIndex="ports"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPorts(record.ports);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title=""
|
||||
align="center"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
danger={true}
|
||||
disabled={
|
||||
deletedPolicy.loading || savedPolicy.loading
|
||||
}
|
||||
onClick={() => showConfirmDelete(record)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>)}
|
||||
{showTutorial && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="center"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "45px 15px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Title level={4} style={{ textAlign: "center" }}>
|
||||
Create New Rule
|
||||
</Title>
|
||||
<Paragraph
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Destinations"
|
||||
dataIndex="destinationLabel"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPopoverGroups(
|
||||
text,
|
||||
record.destinations,
|
||||
record as PolicyDataTable
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Protocol"
|
||||
dataIndex="protocol"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return (
|
||||
<Tag
|
||||
className="menlo-font"
|
||||
style={{
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-line",
|
||||
marginRight: "3",
|
||||
textTransform: "uppercase",
|
||||
}}
|
||||
>
|
||||
It looks like you don't have any rules. {"\n"}
|
||||
Get started with access control by adding a new one.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
<Button
|
||||
size={"middle"}
|
||||
type="primary"
|
||||
onClick={() => onClickAddNewPolicy()}
|
||||
>
|
||||
Add rule
|
||||
</Button>
|
||||
</Space>
|
||||
{record.protocol}
|
||||
</Tag>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Ports"
|
||||
dataIndex="ports"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return renderPorts(record.ports);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title=""
|
||||
align="center"
|
||||
render={(text, record: PolicyDataTable, index) => {
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
danger={true}
|
||||
disabled={
|
||||
deletedPolicy.loading || savedPolicy.loading
|
||||
}
|
||||
onClick={() => showConfirmDelete(record)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
{showTutorial && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="center"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "45px 15px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Title level={4} style={{ textAlign: "center" }}>
|
||||
Create New Rule
|
||||
</Title>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-line",
|
||||
}}
|
||||
>
|
||||
It looks like you don't have any rules. {"\n"}
|
||||
Get started with access control by adding a new one.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-network-access"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
<Button
|
||||
size={"middle"}
|
||||
type="primary"
|
||||
onClick={() => onClickAddNewPolicy()}
|
||||
>
|
||||
Add rule
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</Card>
|
||||
</Space>
|
||||
|
||||
+595
-493
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user