Compare commits

..
3 Commits
19 changed files with 2469 additions and 1403 deletions

No files matched your search

+1 -1
View File
@@ -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
+11
View File
@@ -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"
}
+56 -73
View File
@@ -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
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;
+200 -125
View File
@@ -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
View File
@@ -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;
}
+12 -12
View File
@@ -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;
}
+2 -1
View File
@@ -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>()
};
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff