Compare commits

...
6 Commits
23 changed files with 3463 additions and 2341 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"
}
File diff suppressed because it is too large. Load diff
+83 -62
View File
@@ -29,7 +29,7 @@ import { uniq } from "lodash";
import { Header } from "antd/es/layout/layout";
import { RuleObject } from "antd/lib/form";
import { useGetTokenSilently } from "../utils/token";
import {useGetGroupTagHelpers} from "../utils/groups";
import { useGetGroupTagHelpers } from "../utils/groups";
const { Paragraph } = Typography;
const { Option } = Select;
@@ -50,11 +50,8 @@ interface FormPolicy {
}
const AccessControlNew = () => {
const {
optionRender,
grayTagRender,
blueTagRender
} = useGetGroupTagHelpers()
const { optionRender, grayTagRender, blueTagRender } =
useGetGroupTagHelpers();
const { getTokenSilently } = useGetTokenSilently();
const dispatch = useDispatch();
const setupNewPolicyVisible = useSelector(
@@ -71,6 +68,8 @@ const AccessControlNew = () => {
{ label: "UDP", value: "udp" },
{ label: "ICMP", value: "icmp" },
];
const formPolicyCopy: any = ["ALL"];
const policy = useSelector((state: RootState) => state.policy.policy);
const savedPolicy = useSelector(
(state: RootState) => state.policy.savedPolicy
@@ -274,9 +273,7 @@ const AccessControlNew = () => {
<Divider style={{ margin: "8px 0" }} />
<Row style={{ padding: "0 8px 4px" }}>
<Col flex="auto">
<Text type={"secondary"}>
Add new ports by pressing "Enter"
</Text>
<Text type={"secondary"}>Add new ports by pressing "Enter"</Text>
</Col>
<Col flex="none">
<svg
@@ -460,18 +457,18 @@ const AccessControlNew = () => {
whiteSpace: "pre-line",
fontSize: "18px",
margin: "0px",
fontWeight:"500"
fontWeight: "500",
}}
>
Create Rule
</Paragraph>
<Paragraph
type={"secondary"}
style={{
textAlign: "start",
whiteSpace: "pre-line",
paddingBottom: "15px",
}}
type={"secondary"}
style={{
textAlign: "start",
whiteSpace: "pre-line",
paddingBottom: "15px",
}}
>
Use this rule to restrict access to groups of resources
</Paragraph>
@@ -727,10 +724,11 @@ const AccessControlNew = () => {
</Col>
<Col span={24}>
<Paragraph
type={"secondary"}
style={{ marginTop: "-15px", marginBottom: "30px" }}
type={"secondary"}
style={{ marginTop: "-15px", marginBottom: "30px" }}
>
To change traffic direction and ports, select TCP or UDP protocol below
To change traffic direction and ports, select TCP or UDP
protocol below
</Paragraph>
</Col>
<Col span={24}>
@@ -784,50 +782,73 @@ const AccessControlNew = () => {
Allow network traffic and access only to specified ports
</Paragraph>
</div>
<Form.Item
name="ports"
label=""
style={{ fontWeight: "500" }}
rules={[
{
message: "Directional traffic requires at least one port",
validator: selectPortProtocolValidator,
required: false,
},
{
message: "Port value must be in 1..65535 range",
validator: selectPortRangeValidator,
required: false,
},
]}
>
<Select
mode="tags"
style={{
width: "100%",
maxWidth: "280px",
fontWeight: "500",
}}
className="menlo-font"
placeholder={ (formPolicy.protocol === "all" || formPolicy.protocol === "icmp") ? "Change protocol to add ports" : "Add ports"}
tagRender={grayTagRender}
onChange={handleChangePorts}
dropdownRender={dropDownRenderPorts}
disabled={
formPolicy.protocol === "all" ||
formPolicy.protocol === "icmp"
}
{formPolicy.protocol === "all" ||
formPolicy.protocol === "icmp" ? (
<Form.Item>
<Select
mode="tags"
style={{
width: "100%",
maxWidth: "260px",
fontWeight: "500",
}}
placeholder={<Text>Select ports</Text>}
className="menlo-font"
value={formPolicyCopy}
disabled={true}
></Select>
</Form.Item>
) : (
<Form.Item
name="ports"
label=""
style={{ fontWeight: "500" }}
rules={[
{
message:
"Directional traffic requires at least one port",
validator: selectPortProtocolValidator,
required: false,
},
{
message: "Port value must be in 1..65535 range",
validator: selectPortRangeValidator,
required: false,
},
]}
>
{formPolicy &&
formPolicy.ports?.map((m) => (
<Option key={m}>
<Tag style={{ marginRight: 3 }}>
{m}
</Tag>
</Option>
))}
</Select>
</Form.Item>
<Select
mode="tags"
style={{
width: "100%",
maxWidth: "280px",
fontWeight: "500",
}}
className="menlo-font"
placeholder={
formPolicy.protocol === "all" ||
formPolicy.protocol === "icmp"
? "All"
: (<div color={"rgba(0,0,0,0.25)"} className="arimo-font">Select ports</div>)
}
tagRender={grayTagRender}
onChange={handleChangePorts}
dropdownRender={dropDownRenderPorts}
disabled={
formPolicy.protocol === "all" ||
formPolicy.protocol === "icmp"
}
>
{formPolicy &&
formPolicy.ports?.map((m) => (
<Option key={m}>
<Tag style={{ marginRight: 3 }}>{m}</Tag>
</Option>
))}
</Select>
</Form.Item>
)}
</Col>
<Col span={24}>
<Form.Item name="enabled" label="">
+13 -2
View File
@@ -863,15 +863,26 @@ const PeerUpdate = () => {
</Form.Item>
</Col>
<Col span={8}>
<Paragraph
style={{
whiteSpace: "pre-line",
margin: 0,
fontWeight: "500",
}}
>
Groups
</Paragraph>
<Text type={"secondary"}>
Use groups to control what this peer can access
</Text>
<Form.Item
name="groupsNames"
label="Select peer groups"
rules={[{ validator: selectValidator }]}
style={{ fontWeight: "500" }}
>
<Select
mode="tags"
style={{ width: "100%" }}
style={{ width: "100%", marginTop: 5 }}
placeholder="Select groups..."
tagRender={tagRender}
dropdownRender={dropDownRender}
+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
+3 -25
View File
@@ -39,9 +39,7 @@ const { Text } = Typography;
const ExpiresInDefault: ExpiresInValue = { number: 30, interval: "day" };
const SetupKeyNew = () => {
const {
optionRender,
} = useGetGroupTagHelpers()
const { optionRender, blueTagRender } = useGetGroupTagHelpers();
const { getTokenSilently } = useGetTokenSilently();
const dispatch = useDispatch();
const setupNewKeyVisible = useSelector(
@@ -227,26 +225,6 @@ const SetupKeyNew = () => {
return Promise.resolve();
};
const tagRender = (props: CustomTagProps) => {
const { value, closable, onClose } = props;
const onPreventMouseDown = (event: React.MouseEvent<HTMLSpanElement>) => {
event.preventDefault();
event.stopPropagation();
};
return (
<Tag
color="blue"
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{ marginRight: 3 }}
>
<strong>{value}</strong>
</Tag>
);
};
const dropDownRender = (menu: React.ReactElement) => (
<>
{menu}
@@ -556,9 +534,9 @@ const SetupKeyNew = () => {
>
<Select
mode="tags"
style={{ width: "100%" }}
style={{ width: "100%"}}
placeholder="Associate groups with the key"
tagRender={tagRender}
tagRender={blueTagRender}
dropdownRender={dropDownRender}
// enabled only when we have a new key !setupkey.id or when the key is valid
disabled={!(!setupKey.id || setupKey.valid)}
@@ -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;
}
+173 -3
View File
@@ -2,6 +2,17 @@
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;500&display=swap');
@import 'antd/dist/reset.css';
pre,
code,
kbd,
samp,
pre *,
code *,
kbd *,
samp * {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;
}
html,
body,
* {
@@ -19,7 +30,6 @@ body {
padding: 0;
height: auto;
min-height: 64px;
border-bottom: 1px solid #e0e0e0;
}
@@ -191,6 +201,10 @@ td.non-highlighted-table-column {
cursor: pointer;
}
.cursor-pointer {
cursor: pointer;
}
.ant-modal-content {
border-radius: 0px !important;
}
@@ -204,6 +218,11 @@ td.non-highlighted-table-column {
font-family: 'Menlo', monospace !important;
}
.arimo-font,
.arimo-font * {
font-family: 'Arimo', sans-serif !important;
}
.tag-box .ant-select-selector {
padding: 0 5px !important;
}
@@ -269,6 +288,157 @@ 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;
}
.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),
]);
}
Loaded 20 of 23 files, more files were not shown because too many files have changed in this diff. Show more