mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf42dd52fc | ||
|
|
bc6842e5b5 | ||
|
|
a8ed755dda | ||
|
|
a87c06ef52 | ||
|
|
c0130d265c | ||
|
|
63ced3088a | ||
|
|
42b7a15466 | ||
|
|
c88bfa6476 | ||
|
|
c4138a8c45 | ||
|
|
3f5418bebc | ||
|
|
f60605e5e3 | ||
|
|
c1b2ededa7 | ||
|
|
31eaa4a241 | ||
|
|
ba365336ff | ||
|
|
06c238b013 | ||
|
|
ed56c240f3 | ||
|
|
1f3c7d87d7 | ||
|
|
9de2906fb2 | ||
|
|
359b443326 | ||
|
|
ecae39d94b | ||
|
|
30b858c1bc | ||
|
|
e82b269ff5 | ||
|
|
23a4b79f01 | ||
|
|
cc5a9b1033 | ||
|
|
09ae157be3 | ||
|
|
cb89eeb921 | ||
|
|
79446c0e77 | ||
|
|
1f258e4e2c | ||
|
|
bae95d2e11 | ||
|
|
3d95a826e7 | ||
|
|
d8d13aff01 | ||
|
|
695b571a50 | ||
|
|
ddfb6a6179 | ||
|
|
8c94119c6a | ||
|
|
439e803ef2 | ||
|
|
440c51a35c | ||
|
|
800e94de85 | ||
|
|
ba7d138156 | ||
|
|
a66fb3bf8f | ||
|
|
fcc51243e0 | ||
|
|
312c60dd45 | ||
|
|
09e6de74ee |
No files matched your search
+1
-1
@@ -26,6 +26,6 @@ yarn-error.log*
|
||||
src/auth_config.json
|
||||
.idea
|
||||
.eslintcache
|
||||
src/.local-config.json
|
||||
src/.local-config*.json
|
||||
/public/OidcServiceWorker.js
|
||||
/public/OidcTrustedDomains.js
|
||||
@@ -22,6 +22,10 @@ if [[ -z "${AUTH_AUDIENCE}" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${AUTH_AUDIENCE}" == "none" ]]; then
|
||||
unset AUTH_AUDIENCE
|
||||
fi
|
||||
|
||||
if [[ -z "${AUTH_SUPPORTED_SCOPES}" ]]; then
|
||||
if [[ -z "${AUTH0_DOMAIN}" ]]; then
|
||||
echo "AUTH_SUPPORTED_SCOPES environment variable must be set"
|
||||
@@ -54,11 +58,12 @@ export NETBIRD_MGMT_API_ENDPOINT=$(echo $NETBIRD_MGMT_API_ENDPOINT | sed -E 's/(
|
||||
export NETBIRD_MGMT_GRPC_API_ENDPOINT=${NETBIRD_MGMT_GRPC_API_ENDPOINT}
|
||||
export NETBIRD_HOTJAR_TRACK_ID=${NETBIRD_HOTJAR_TRACK_ID}
|
||||
export NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
|
||||
export NETBIRD_DRAG_QUERY_PARAMS=${NETBIRD_DRAG_QUERY_PARAMS:-false}
|
||||
|
||||
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
|
||||
|
||||
# replace ENVs in the config
|
||||
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE"
|
||||
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
|
||||
|
||||
MAIN_JS=$(find /usr/share/nginx/html/static/js/main.*js)
|
||||
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
|
||||
|
||||
Generated
+471
-96
File diff suppressed because it is too large.
Load diff
@@ -39,6 +39,7 @@
|
||||
"react-redux": "^8.0.2",
|
||||
"react-router-dom": "^5.3.3",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-select": "^5.7.3",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-table": "^7.7.0",
|
||||
"redux": "^4.2.0",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
+1
-12
@@ -70,16 +70,7 @@ function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
borderRadius: 4,
|
||||
colorPrimary: "#1890ff",
|
||||
fontFamily: "Arial"
|
||||
},
|
||||
components: {Badge: {fontSizeSM: 20}},
|
||||
}}
|
||||
>
|
||||
|
||||
<Provider store={store}>
|
||||
{!show && <SecureLoading padding="3em" width={50} height={50}/>}
|
||||
{show &&
|
||||
@@ -124,8 +115,6 @@ function App() {
|
||||
</Layout>
|
||||
}
|
||||
</Provider>
|
||||
|
||||
</ConfigProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
@@ -52,9 +52,10 @@ interface FormPolicy {
|
||||
const AccessControlNew = () => {
|
||||
const {
|
||||
optionRender,
|
||||
grayTagRender,
|
||||
blueTagRender
|
||||
} = useGetGroupTagHelpers()
|
||||
blueTagRender,
|
||||
tagGroups,
|
||||
grayTagRender,
|
||||
} = useGetGroupTagHelpers();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
const setupNewPolicyVisible = useSelector(
|
||||
@@ -71,6 +72,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
|
||||
@@ -82,7 +85,7 @@ const AccessControlNew = () => {
|
||||
biDirectional: true,
|
||||
reverseDirectional: true,
|
||||
});
|
||||
const [tagGroups, setTagGroups] = useState([] as string[]);
|
||||
// const [tagGroups, setTagGroups] = useState([] as string[]);
|
||||
const [formPolicy, setFormPolicy] = useState({} as FormPolicy);
|
||||
const [form] = Form.useForm();
|
||||
const inputNameRef = useRef<any>(null);
|
||||
@@ -94,9 +97,9 @@ const AccessControlNew = () => {
|
||||
useEffect(() => {
|
||||
if (editDescription) inputDescriptionRef.current!.focus({ cursor: "end" });
|
||||
}, [editDescription]);
|
||||
useEffect(() => {
|
||||
setTagGroups(groups?.map((g) => g.name) || []);
|
||||
}, [groups]);
|
||||
// useEffect(() => {
|
||||
// setTagGroups(groups?.map((g) => g.name) || []);
|
||||
// }, [groups]);
|
||||
useEffect(() => {
|
||||
if (!policy) return;
|
||||
const fPolicy = {
|
||||
@@ -123,19 +126,26 @@ const AccessControlNew = () => {
|
||||
const createPolicyToSave = (): PolicyToSave => {
|
||||
const sources =
|
||||
groups
|
||||
?.filter((g) => formPolicy.tagSourceGroups.includes(g.name))
|
||||
?.filter((g) => formPolicy.tagSourceGroups.includes(g.id || ""))
|
||||
.map((g) => g.id || "") || [];
|
||||
|
||||
const destinations =
|
||||
groups
|
||||
?.filter((g) => formPolicy.tagDestinationGroups.includes(g.name))
|
||||
?.filter((g) => formPolicy.tagDestinationGroups.includes(g.id || ""))
|
||||
.map((g) => g.id || "") || [];
|
||||
|
||||
|
||||
const existingGroupsNames: any[] = groups?.map((g) => g.id);
|
||||
const sourcesNoId = formPolicy.tagSourceGroups.filter(
|
||||
(s) => !tagGroups.includes(s)
|
||||
(s) => !existingGroupsNames.includes(s)
|
||||
);
|
||||
|
||||
const destinationsNoId = formPolicy.tagDestinationGroups.filter(
|
||||
(s) => !tagGroups.includes(s)
|
||||
(s) => !existingGroupsNames.includes(s)
|
||||
);
|
||||
|
||||
const groupsToSave = uniq([...sourcesNoId, ...destinationsNoId]);
|
||||
|
||||
return {
|
||||
id: formPolicy.id,
|
||||
name: formPolicy.name,
|
||||
@@ -274,9 +284,7 @@ const AccessControlNew = () => {
|
||||
<Divider style={{ margin: "8px 0" }} />
|
||||
<Row style={{ padding: "0 8px 4px" }}>
|
||||
<Col flex="auto">
|
||||
<Text type={"secondary"}>
|
||||
Add new ports by pressing "Enter"
|
||||
</Text>
|
||||
<Text type={"secondary"}>Add new ports by pressing "Enter"</Text>
|
||||
</Col>
|
||||
<Col flex="none">
|
||||
<svg
|
||||
@@ -458,19 +466,22 @@ const AccessControlNew = () => {
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
fontSize: "22px",
|
||||
margin: "0px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Create Rule
|
||||
Create rule
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
paddingBottom: "15px",
|
||||
}}
|
||||
type={"secondary"}
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
marginTop: "-20px",
|
||||
fontSize: "14px",
|
||||
paddingBottom: "25px",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
Use this rule to restrict access to groups of resources
|
||||
</Paragraph>
|
||||
@@ -480,7 +491,7 @@ const AccessControlNew = () => {
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Rule name
|
||||
@@ -531,7 +542,7 @@ const AccessControlNew = () => {
|
||||
<Form.Item
|
||||
name="description"
|
||||
label="Description"
|
||||
style={{ marginTop: 24, fontWeight: "600" }}
|
||||
style={{ marginTop: 24, fontWeight: "500" }}
|
||||
>
|
||||
<Input
|
||||
placeholder="Add description..."
|
||||
@@ -553,7 +564,7 @@ const AccessControlNew = () => {
|
||||
name="tagSourceGroups"
|
||||
label="Source"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
style={{ fontWeight: "600" }}
|
||||
style={{ fontWeight: "500" }}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
@@ -562,9 +573,12 @@ const AccessControlNew = () => {
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleChangeSource}
|
||||
dropdownRender={dropDownRenderGroups}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m) => (
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@@ -706,7 +720,7 @@ const AccessControlNew = () => {
|
||||
name="tagDestinationGroups"
|
||||
label="Destination"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
style={{ fontWeight: "600" }}
|
||||
style={{ fontWeight: "500" }}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
@@ -715,9 +729,12 @@ const AccessControlNew = () => {
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleChangeDestination}
|
||||
dropdownRender={dropDownRenderGroups}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m) => (
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@@ -726,24 +743,25 @@ 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}>
|
||||
<Form.Item
|
||||
name="protocol"
|
||||
label="Protocol"
|
||||
style={{ fontWeight: "600" }}
|
||||
style={{ fontWeight: "500" }}
|
||||
className="tag-box"
|
||||
>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-10px",
|
||||
fontWeight: "500",
|
||||
fontWeight: "400",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
@@ -767,7 +785,9 @@ const AccessControlNew = () => {
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "600",
|
||||
fontWeight: "500",
|
||||
padding: "0 0 2px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
Ports
|
||||
@@ -776,57 +796,87 @@ const AccessControlNew = () => {
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-5px",
|
||||
fontWeight: "500",
|
||||
fontWeight: "400",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
Allow network traffic and access only to specified ports
|
||||
</Paragraph>
|
||||
</div>
|
||||
<Form.Item
|
||||
name="ports"
|
||||
label=""
|
||||
style={{ fontWeight: "600" }}
|
||||
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="">
|
||||
@@ -837,6 +887,7 @@ const AccessControlNew = () => {
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
size="small"
|
||||
onChange={handleChangeDisabled}
|
||||
defaultChecked={true}
|
||||
/>
|
||||
@@ -845,7 +896,7 @@ const AccessControlNew = () => {
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Enabled
|
||||
@@ -854,7 +905,7 @@ const AccessControlNew = () => {
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "500",
|
||||
fontWeight: "400",
|
||||
marginBottom: "0",
|
||||
}}
|
||||
>
|
||||
@@ -868,7 +919,7 @@ const AccessControlNew = () => {
|
||||
</Col>
|
||||
<Col
|
||||
span={24}
|
||||
style={{ marginTop: "20px", marginBottom: "25px" }}
|
||||
style={{ marginTop: "10px", marginBottom: "24px" }}
|
||||
>
|
||||
<Text type={"secondary"}>
|
||||
Learn more about
|
||||
|
||||
@@ -6,7 +6,7 @@ const { Footer } = Layout
|
||||
export default () => {
|
||||
return (
|
||||
<Footer style={{ textAlign: 'center', bottom: "0"}}>
|
||||
Copyright © 2022 <a href="https://netbird.io">NetBird Authors</a>
|
||||
Copyright © 2023 <a href="https://netbird.io">NetBird Authors</a>
|
||||
</Footer>
|
||||
);
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+225
-167
@@ -1,185 +1,243 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Link, useLocation} from 'react-router-dom';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import logo from "../assets/logo.png";
|
||||
import {Avatar, Button, Col, Dropdown, Grid, Menu, Row} from 'antd'
|
||||
import {ItemType} from "antd/lib/menu/hooks/useItems";
|
||||
import {AvatarSize} from "antd/es/avatar/SizeContext";
|
||||
import {UserOutlined} from '@ant-design/icons';
|
||||
import {useOidc, useOidcIdToken, useOidcUser} from '@axa-fr/react-oidc';
|
||||
import {getConfig} from "../config";
|
||||
import {User} from "../store/user/types";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {RootState} from "typesafe-actions";
|
||||
import {actions as userActions} from "../store/user";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {actions as personalAccessTokenActions} from "../store/personal-access-token";
|
||||
import { Avatar, Button, Col, Dropdown, Grid, Menu, Row } from "antd";
|
||||
import { ItemType } from "antd/lib/menu/hooks/useItems";
|
||||
import { AvatarSize } from "antd/es/avatar/SizeContext";
|
||||
import { UserOutlined } from "@ant-design/icons";
|
||||
import { useOidc, useOidcIdToken, useOidcUser } from "@axa-fr/react-oidc";
|
||||
import { getConfig } from "../config";
|
||||
import { User } from "../store/user/types";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import { actions as userActions } from "../store/user";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
import { actions as personalAccessTokenActions } from "../store/personal-access-token";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
const Navbar = () => {
|
||||
let location = useLocation();
|
||||
const config = getConfig();
|
||||
const { logout } = useOidc();
|
||||
const {getTokenSilently} = useGetTokenSilently()
|
||||
const dispatch = useDispatch()
|
||||
let location = useLocation();
|
||||
const config = getConfig();
|
||||
const { logout } = useOidc();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {oidcUser} = useOidcUser();
|
||||
const {idTokenPayload} = useOidcIdToken()
|
||||
const user = oidcUser;
|
||||
const [currentUser, setCurrentUser] = useState({} as User)
|
||||
const { oidcUser } = useOidcUser();
|
||||
const { idTokenPayload } = useOidcIdToken();
|
||||
const user = oidcUser;
|
||||
const [currentUser, setCurrentUser] = useState({} as User);
|
||||
|
||||
const screens = useBreakpoint();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [hideMenuUser, setHideMenuUser] = useState(false)
|
||||
const users = useSelector((state: RootState) => state.user.data)
|
||||
const [isRefreshingUserState, setIsRefreshingUserState] = useState(false)
|
||||
const [hideMenuUser, setHideMenuUser] = useState(false);
|
||||
const users = useSelector((state: RootState) => state.user.data);
|
||||
const [isRefreshingUserState, setIsRefreshingUserState] = useState(false);
|
||||
|
||||
const items = [
|
||||
{label: (<Link to="/peers">Peers</Link>), key: '/peers'},
|
||||
{label: (<Link to="/setup-keys">Setup Keys</Link>), key: '/setup-keys'},
|
||||
{label: (<Link to="/acls">Access Control</Link>), key: '/acls'},
|
||||
{label: (<Link to="/routes">Network Routes</Link>), key: '/routes'},
|
||||
{ label: (<Link to="/dns">DNS</Link>), key: '/dns' },
|
||||
{label: (<Link to="/users">Users</Link>), key: '/users'},
|
||||
{label: (<Link to="/activity">Activity</Link>), key: '/activity'},
|
||||
{label: (<Link to="/settings">Settings</Link>), key: '/settings'}
|
||||
] as ItemType[]
|
||||
const items = [
|
||||
{ label: <Link to="/peers">Peers</Link>, key: "/peers" },
|
||||
{ label: <Link to="/setup-keys">Setup Keys</Link>, key: "/setup-keys" },
|
||||
{ label: <Link to="/acls">Access Control</Link>, key: "/acls" },
|
||||
{ label: <Link to="/routes">Network Routes</Link>, key: "/routes" },
|
||||
{ label: <Link to="/dns">DNS</Link>, key: "/dns" },
|
||||
{ label: <Link to="/users">Users</Link>, key: "/users" },
|
||||
{ label: <Link to="/activity">Activity</Link>, key: "/activity" },
|
||||
{ label: <Link to="/settings">Settings</Link>, key: "/settings" },
|
||||
] as ItemType[];
|
||||
|
||||
const userEmailKey = 'user-email'
|
||||
const userLogoutKey = 'user-logout'
|
||||
const userDividerKey = 'user-divider'
|
||||
const adminOnlyTabs = ["/setup-keys", "/acls", "/routes", "/dns", "/activity", "/settings"]
|
||||
const [menuItems, setMenuItems] = useState(items)
|
||||
const logoutWithRedirect = () =>
|
||||
logout("/", {client_id: config.clientId});
|
||||
const userEmailKey = "user-email";
|
||||
const userLogoutKey = "user-logout";
|
||||
const userDividerKey = "user-divider";
|
||||
const adminOnlyTabs = [
|
||||
"/setup-keys",
|
||||
"/acls",
|
||||
"/routes",
|
||||
"/dns",
|
||||
"/activity",
|
||||
"/settings",
|
||||
];
|
||||
const [menuItems, setMenuItems] = useState(items);
|
||||
const logoutWithRedirect = () => {
|
||||
let lRemove = [
|
||||
"peerFilter",
|
||||
"setupKeysFilter",
|
||||
"accessControlFilter",
|
||||
"routesFilter",
|
||||
"nameServerFilter",
|
||||
"userFilter",
|
||||
"serviceUserFilter",
|
||||
"activityFilter",
|
||||
];
|
||||
lRemove.forEach((element) => {
|
||||
localStorage.removeItem(element);
|
||||
});
|
||||
logout("/", { client_id: config.clientId });
|
||||
};
|
||||
|
||||
const openPersonalUserPage = () => {
|
||||
dispatch(userActions.setUser({
|
||||
id: currentUser.id,
|
||||
email: currentUser.email,
|
||||
role: currentUser.role,
|
||||
auto_groups: currentUser.auto_groups ? currentUser.auto_groups : [],
|
||||
name: currentUser.name,
|
||||
is_current: currentUser.is_current,
|
||||
is_service_user: currentUser.is_service_user,
|
||||
} as User));
|
||||
dispatch(userActions.setUserTabOpen("Users"));
|
||||
dispatch(userActions.setEditUserPopupVisible(true));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fs = items.filter(m => showTab(m?.key?.toString(), currentUser) && m?.key !== userEmailKey && m?.key !== userLogoutKey && m?.key !== userDividerKey)
|
||||
if (screens.xs === true) {
|
||||
setHideMenuUser(false)
|
||||
fs.push({type: 'divider', key: userDividerKey})
|
||||
fs.push({
|
||||
label: (
|
||||
<Link to="#">{user?.name}</Link>
|
||||
),
|
||||
icon: createAvatar("small"),
|
||||
key: userEmailKey
|
||||
})
|
||||
fs.push({
|
||||
label: (<Button type="link" block onClick={logoutWithRedirect}>Logout</Button>),
|
||||
key: userLogoutKey
|
||||
})
|
||||
setMenuItems([...fs])
|
||||
return
|
||||
}
|
||||
setMenuItems([...fs])
|
||||
setHideMenuUser(true)
|
||||
}, [screens, currentUser])
|
||||
|
||||
useEffect(() => {
|
||||
if (users.length === 0 && !isRefreshingUserState &&
|
||||
window.location.pathname !== '/peers' &&
|
||||
window.location.pathname !== '/users') {
|
||||
setIsRefreshingUserState(true)
|
||||
dispatch(userActions.getUsers.request({getAccessTokenSilently: getTokenSilently, payload: null}))
|
||||
return
|
||||
}
|
||||
if (users.length === 0 && isRefreshingUserState) {
|
||||
return
|
||||
}
|
||||
let runUser = oidcUser
|
||||
if (!oidcUser) {
|
||||
runUser = idTokenPayload
|
||||
}
|
||||
setIsRefreshingUserState(false)
|
||||
if (runUser) {
|
||||
const found = users.find(u => u.is_current ? u.is_current : runUser.sub ? u.id == runUser.sub : false)
|
||||
if (found) {
|
||||
setCurrentUser(found)
|
||||
}
|
||||
}
|
||||
}, [users, oidcUser])
|
||||
|
||||
const showTab = (key: string | undefined, user: User | undefined) => {
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (user.role?.toLowerCase() === "admin") {
|
||||
return true
|
||||
}
|
||||
return !adminOnlyTabs.find(t => t === key)
|
||||
}
|
||||
|
||||
const menuUser = (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
label: (<Link to="/users" onClick={openPersonalUserPage}>{user?.email}</Link>),
|
||||
key: '0',
|
||||
},
|
||||
{
|
||||
label: (<Link to="/logout" onClick={logoutWithRedirect}>Logout</Link>),
|
||||
key: '1',
|
||||
}
|
||||
]}
|
||||
/>
|
||||
const openPersonalUserPage = () => {
|
||||
dispatch(
|
||||
userActions.setUser({
|
||||
id: currentUser.id,
|
||||
email: currentUser.email,
|
||||
role: currentUser.role,
|
||||
auto_groups: currentUser.auto_groups ? currentUser.auto_groups : [],
|
||||
name: currentUser.name,
|
||||
is_current: currentUser.is_current,
|
||||
is_service_user: currentUser.is_service_user,
|
||||
} as User)
|
||||
);
|
||||
dispatch(userActions.setUserTabOpen("Users"));
|
||||
dispatch(userActions.setEditUserPopupVisible(true));
|
||||
};
|
||||
|
||||
const createAvatar = (size: AvatarSize) => {
|
||||
return user?.picture ? (
|
||||
<Avatar size={size} src={user?.picture} icon={<UserOutlined/>}/>
|
||||
) : (
|
||||
<Avatar size={size}>{(user?.name || '').slice(0, 1).toUpperCase()}</Avatar>
|
||||
)
|
||||
useEffect(() => {
|
||||
const fs = items.filter(
|
||||
(m) =>
|
||||
showTab(m?.key?.toString(), currentUser) &&
|
||||
m?.key !== userEmailKey &&
|
||||
m?.key !== userLogoutKey &&
|
||||
m?.key !== userDividerKey
|
||||
);
|
||||
if (screens.xs === true) {
|
||||
setHideMenuUser(false);
|
||||
fs.push({ type: "divider", key: userDividerKey });
|
||||
fs.push({
|
||||
label: <Link to="#">{user?.name}</Link>,
|
||||
icon: createAvatar("small"),
|
||||
key: userEmailKey,
|
||||
});
|
||||
fs.push({
|
||||
label: (
|
||||
<Button type="link" block onClick={logoutWithRedirect}>
|
||||
Logout
|
||||
</Button>
|
||||
),
|
||||
key: userLogoutKey,
|
||||
});
|
||||
setMenuItems([...fs]);
|
||||
return;
|
||||
}
|
||||
setMenuItems([...fs]);
|
||||
setHideMenuUser(true);
|
||||
}, [screens, currentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
users.length === 0 &&
|
||||
!isRefreshingUserState &&
|
||||
window.location.pathname !== "/peers" &&
|
||||
window.location.pathname !== "/users"
|
||||
) {
|
||||
setIsRefreshingUserState(true);
|
||||
dispatch(
|
||||
userActions.getUsers.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (users.length === 0 && isRefreshingUserState) {
|
||||
return;
|
||||
}
|
||||
let runUser = oidcUser;
|
||||
if (!oidcUser) {
|
||||
runUser = idTokenPayload;
|
||||
}
|
||||
setIsRefreshingUserState(false);
|
||||
if (runUser) {
|
||||
const found = users.find((u) =>
|
||||
u.is_current ? u.is_current : runUser.sub ? u.id == runUser.sub : false
|
||||
);
|
||||
if (found) {
|
||||
setCurrentUser(found);
|
||||
}
|
||||
}
|
||||
}, [users, oidcUser]);
|
||||
|
||||
const showTab = (key: string | undefined, user: User | undefined) => {
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row justify="space-evenly" align="middle">
|
||||
<Col flex="0 1 60px">
|
||||
<Link id="logo" to="/">
|
||||
<img
|
||||
alt="logo"
|
||||
style={{width: "55px"}}
|
||||
src={logo}
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col flex="1 1 auto">
|
||||
<div>
|
||||
<Menu mode="horizontal" selectable={true} selectedKeys={[location.pathname]}
|
||||
onSelect={(e) => {
|
||||
dispatch(userActions.setUser(null as unknown as User));
|
||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null))
|
||||
}}
|
||||
defaultSelectedKeys={[location.pathname]} items={menuItems}/>
|
||||
</div>
|
||||
</Col>
|
||||
{hideMenuUser &&
|
||||
<Col>
|
||||
<Dropdown overlay={menuUser} placement="bottomRight" trigger={['click']}>
|
||||
{createAvatar("large")}
|
||||
</Dropdown>
|
||||
</Col>
|
||||
}
|
||||
</Row>
|
||||
</>
|
||||
if (user.role?.toLowerCase() === "admin") {
|
||||
return true;
|
||||
}
|
||||
return !adminOnlyTabs.find((t) => t === key);
|
||||
};
|
||||
|
||||
const menuUser = (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
label: (
|
||||
<Link to="/users" onClick={openPersonalUserPage}>
|
||||
{user?.email}
|
||||
</Link>
|
||||
),
|
||||
key: "0",
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Link to="/logout" onClick={logoutWithRedirect}>
|
||||
Logout
|
||||
</Link>
|
||||
),
|
||||
key: "1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const createAvatar = (size: AvatarSize) => {
|
||||
return user?.picture ? (
|
||||
<Avatar size={size} src={user?.picture} icon={<UserOutlined />} />
|
||||
) : (
|
||||
<Avatar size={size}>
|
||||
{(user?.name || "").slice(0, 1).toUpperCase()}
|
||||
</Avatar>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row justify="space-evenly" align="middle">
|
||||
<Col flex="0 1 60px">
|
||||
<Link id="logo" to="/">
|
||||
<img alt="logo" style={{ width: "55px" }} src={logo} />
|
||||
</Link>
|
||||
</Col>
|
||||
<Col flex="1 1 auto">
|
||||
<div>
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
selectable={true}
|
||||
selectedKeys={[location.pathname]}
|
||||
onSelect={(e) => {
|
||||
dispatch(userActions.setUser(null as unknown as User));
|
||||
dispatch(
|
||||
personalAccessTokenActions.resetPersonalAccessTokens(null)
|
||||
);
|
||||
}}
|
||||
defaultSelectedKeys={[location.pathname]}
|
||||
items={menuItems}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
{hideMenuUser && (
|
||||
<Col>
|
||||
<Dropdown
|
||||
overlay={menuUser}
|
||||
placement="bottomRight"
|
||||
trigger={["click"]}
|
||||
>
|
||||
{createAvatar("large")}
|
||||
</Dropdown>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
+718
-410
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+465
-441
File diff suppressed because it is too large.
Load diff
@@ -7,11 +7,9 @@ import {
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Row,
|
||||
Select,
|
||||
Breadcrumb,
|
||||
Switch,
|
||||
Tag,
|
||||
Typography,
|
||||
Card,
|
||||
@@ -22,22 +20,19 @@ import {
|
||||
SetupKey,
|
||||
SetupKeyToSave,
|
||||
} from "../store/setup-key/types";
|
||||
import { formatDate, timeAgo } from "../utils/common";
|
||||
import { formatDate } from "../utils/common";
|
||||
import { RuleObject } from "antd/lib/form";
|
||||
import { CustomTagProps } from "rc-select/lib/BaseSelect";
|
||||
import { Group } from "../store/group/types";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
import { expiresInToSeconds, ExpiresInValue } from "../views/ExpiresInInput";
|
||||
import moment from "moment";
|
||||
import { Container } from "./Container";
|
||||
import Paragraph from "antd/es/typography/Paragraph";
|
||||
import { EditOutlined, LockOutlined } from "@ant-design/icons";
|
||||
import { LockOutlined } from "@ant-design/icons";
|
||||
import { actions as personalAccessTokenActions } from "../store/personal-access-token";
|
||||
import {useGetGroupTagHelpers} from "../utils/groups";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
|
||||
const { Option } = Select;
|
||||
const { Text } = Typography;
|
||||
const ExpiresInDefault: ExpiresInValue = { number: 30, interval: "day" };
|
||||
|
||||
const customExpiresFormat = (value: Date): string | null => {
|
||||
return formatDate(value);
|
||||
@@ -46,8 +41,11 @@ const customExpiresFormat = (value: Date): string | null => {
|
||||
const SetupKeyNew = () => {
|
||||
const {
|
||||
optionRender,
|
||||
blueTagRender
|
||||
} = useGetGroupTagHelpers()
|
||||
blueTagRender,
|
||||
tagGroups,
|
||||
getExistingAndToCreateGroupsLists,
|
||||
setGroupTagFilterAll,
|
||||
} = useGetGroupTagHelpers();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -55,33 +53,31 @@ const SetupKeyNew = () => {
|
||||
const savedSetupKey = useSelector(
|
||||
(state: RootState) => state.setupKey.savedSetupKey
|
||||
);
|
||||
|
||||
const groups = useSelector((state: RootState) => state.group.data);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [editName, setEditName] = useState(false);
|
||||
const [tagGroups, setTagGroups] = useState([] as string[]);
|
||||
const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey);
|
||||
const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey);
|
||||
const inputNameRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
//Unmounting component clean
|
||||
return () => {
|
||||
setVisibleNewSetupKey(false)
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setGroupTagFilterAll(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
//Unmounting component clean
|
||||
return () => {
|
||||
setVisibleNewSetupKey(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editName) return;
|
||||
|
||||
inputNameRef.current!.focus({ cursor: "end" });
|
||||
}, [editName]);
|
||||
|
||||
useEffect(() => {
|
||||
setTagGroups(
|
||||
groups?.filter((g) => g.name !== "All").map((g) => g.name) || []
|
||||
);
|
||||
}, [groups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!setupKey) return;
|
||||
|
||||
@@ -97,33 +93,25 @@ const SetupKeyNew = () => {
|
||||
|
||||
const fSetupKey = {
|
||||
...setupKey,
|
||||
autoGroupNames: setupKey.auto_groups ? formKeyGroups : [],
|
||||
expiresInFormatted: ExpiresInDefault,
|
||||
autoGroupNames: setupKey.auto_groups || [],
|
||||
exp: moment(setupKey.expires),
|
||||
last: moment(setupKey.last_used),
|
||||
} as FormSetupKey;
|
||||
|
||||
form.setFieldsValue(fSetupKey);
|
||||
setFormSetupKey(fSetupKey);
|
||||
}, [setupKey]);
|
||||
|
||||
const createSetupKeyToSave = (): SetupKeyToSave => {
|
||||
const autoGroups =
|
||||
groups
|
||||
?.filter((g) => formSetupKey.autoGroupNames.includes(g.name))
|
||||
.map((g) => g.id || "") || [];
|
||||
// find groups that do not yet exist (newly added by the user)
|
||||
const allGroupsNames: string[] = groups?.map((g) => g.name);
|
||||
const groupsToCreate = formSetupKey.autoGroupNames.filter(
|
||||
(s) => !allGroupsNames.includes(s)
|
||||
let [existingGroups, groupsToCreate] = getExistingAndToCreateGroupsLists(
|
||||
formSetupKey.autoGroupNames
|
||||
);
|
||||
|
||||
const expiresIn = expiresInToSeconds(formSetupKey.expiresInFormatted);
|
||||
const expiresIn = formSetupKey.expires_in * 24 * 3600; // the api expects seconds while the form returns days
|
||||
return {
|
||||
id: formSetupKey.id,
|
||||
name: formSetupKey.name,
|
||||
type: formSetupKey.type,
|
||||
auto_groups: autoGroups,
|
||||
auto_groups: existingGroups,
|
||||
revoked: formSetupKey.revoked,
|
||||
groupsToCreate: groupsToCreate,
|
||||
expires_in: expiresIn,
|
||||
@@ -269,7 +257,7 @@ const SetupKeyNew = () => {
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb
|
||||
style={{ marginBottom: "30px" }}
|
||||
style={{ marginBottom: "25px" }}
|
||||
items={[
|
||||
{
|
||||
title: <a onClick={onBreadcrumbUsersClick}>Setup Keys</a>,
|
||||
@@ -279,19 +267,23 @@ const SetupKeyNew = () => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Card
|
||||
bordered={true}
|
||||
title={setupKey.name}
|
||||
style={{ marginBottom: "7px" }}
|
||||
>
|
||||
<Card bordered={true} style={{ marginBottom: "7px", border: "none" }}>
|
||||
<div style={{ maxWidth: "800px" }}>
|
||||
<h3
|
||||
style={{
|
||||
fontSize: "22px",
|
||||
fontWeight: "500",
|
||||
marginBottom: "30px",
|
||||
}}
|
||||
>
|
||||
{setupKey.name}
|
||||
</h3>
|
||||
<Form
|
||||
layout="vertical"
|
||||
requiredMark={false}
|
||||
form={form}
|
||||
onValuesChange={onChange}
|
||||
initialValues={{
|
||||
expiresIn: ExpiresInDefault,
|
||||
usage_limit: 1,
|
||||
}}
|
||||
>
|
||||
@@ -309,7 +301,7 @@ const SetupKeyNew = () => {
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
@@ -340,14 +332,14 @@ const SetupKeyNew = () => {
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
{formSetupKey.type === "one-off" ? "One-off" : "Reusable"},
|
||||
@@ -369,7 +361,7 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "30px" }}>
|
||||
<Row style={{ marginTop: "39px" }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
@@ -384,7 +376,7 @@ const SetupKeyNew = () => {
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Auto-assigned groups
|
||||
@@ -404,9 +396,12 @@ const SetupKeyNew = () => {
|
||||
dropdownRender={dropDownRender}
|
||||
// enabled only when we have a new key !setupkey.id or when the key is valid
|
||||
disabled={!(!setupKey.id || setupKey.valid)}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m) => (
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@@ -414,7 +409,7 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Paragraph style={{ margin: 0, fontWeight: "bold" }}>
|
||||
<Paragraph style={{ margin: 0, fontWeight: "500" }}>
|
||||
Expires
|
||||
</Paragraph>
|
||||
<Row>
|
||||
@@ -427,20 +422,6 @@ const SetupKeyNew = () => {
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "40px", marginBottom: "28px" }}>
|
||||
<Text type={"secondary"}>
|
||||
Learn more about
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/register-machines-using-setup-keys"
|
||||
>
|
||||
{" "}
|
||||
setup keys
|
||||
</a>
|
||||
</Text>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
<Container
|
||||
@@ -450,6 +431,7 @@ const SetupKeyNew = () => {
|
||||
justifyContent: "start",
|
||||
padding: 0,
|
||||
gap: "10px",
|
||||
marginTop: "24px",
|
||||
}}
|
||||
key={0}
|
||||
>
|
||||
|
||||
@@ -11,9 +11,7 @@ import {
|
||||
Modal,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Tag,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import { RootState } from "typesafe-actions";
|
||||
@@ -23,25 +21,24 @@ import {
|
||||
SetupKeyToSave,
|
||||
} from "../store/setup-key/types";
|
||||
import { RuleObject } from "antd/lib/form";
|
||||
import { CustomTagProps } from "rc-select/lib/BaseSelect";
|
||||
import { Group } from "../store/group/types";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
import { expiresInToSeconds, ExpiresInValue } from "../views/ExpiresInInput";
|
||||
import moment from "moment";
|
||||
import { Container } from "./Container";
|
||||
import Paragraph from "antd/es/typography/Paragraph";
|
||||
import { CheckOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import { copyToClipboard } from "../utils/common";
|
||||
import {useGetGroupTagHelpers} from "../utils/groups";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
|
||||
const { Option } = Select;
|
||||
const { Text } = Typography;
|
||||
const ExpiresInDefault: ExpiresInValue = { number: 30, interval: "day" };
|
||||
|
||||
const SetupKeyNew = () => {
|
||||
const {
|
||||
optionRender,
|
||||
} = useGetGroupTagHelpers()
|
||||
blueTagRender,
|
||||
tagGroups,
|
||||
getExistingAndToCreateGroupsLists,
|
||||
setGroupTagFilterAll,
|
||||
} = useGetGroupTagHelpers();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
const setupNewKeyVisible = useSelector(
|
||||
@@ -58,7 +55,6 @@ const SetupKeyNew = () => {
|
||||
const [showPlainToken, setShowPlainToken] = useState(false);
|
||||
const [plainToken, setPlainToken] = useState("");
|
||||
const [tokenCopied, setTokenCopied] = useState(false);
|
||||
const [tagGroups, setTagGroups] = useState([] as string[]);
|
||||
const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey);
|
||||
const inputNameRef = useRef<any>(null);
|
||||
|
||||
@@ -69,53 +65,21 @@ const SetupKeyNew = () => {
|
||||
}, [editName]);
|
||||
|
||||
useEffect(() => {
|
||||
setTagGroups(
|
||||
groups?.filter((g) => g.name !== "All").map((g) => g.name) || []
|
||||
);
|
||||
}, [groups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!setupKey) return;
|
||||
|
||||
const allGroups = new Map<string, Group>();
|
||||
let formKeyGroups: string[] = [];
|
||||
groups.forEach((g) => allGroups.set(g.id!, g));
|
||||
|
||||
if (setupKey.auto_groups) {
|
||||
formKeyGroups = setupKey.auto_groups
|
||||
.filter((g) => allGroups.get(g))
|
||||
.map((g) => allGroups.get(g)!.name);
|
||||
}
|
||||
|
||||
const fSetupKey = {
|
||||
...setupKey,
|
||||
autoGroupNames: setupKey.auto_groups ? formKeyGroups : [],
|
||||
expiresInFormatted: ExpiresInDefault,
|
||||
exp: moment(setupKey.expires),
|
||||
last: moment(setupKey.last_used),
|
||||
} as FormSetupKey;
|
||||
|
||||
form.setFieldsValue(fSetupKey);
|
||||
setFormSetupKey(fSetupKey);
|
||||
}, [setupKey]);
|
||||
setGroupTagFilterAll(true);
|
||||
}, []);
|
||||
|
||||
const createSetupKeyToSave = (): SetupKeyToSave => {
|
||||
const autoGroups =
|
||||
groups
|
||||
?.filter((g) => formSetupKey.autoGroupNames.includes(g.name))
|
||||
.map((g) => g.id || "") || [];
|
||||
// find groups that do not yet exist (newly added by the user)
|
||||
const allGroupsNames: string[] = groups?.map((g) => g.name);
|
||||
const groupsToCreate = formSetupKey.autoGroupNames.filter(
|
||||
(s) => !allGroupsNames.includes(s)
|
||||
let [existingGroups, groupsToCreate] = getExistingAndToCreateGroupsLists(
|
||||
formSetupKey.autoGroupNames || []
|
||||
);
|
||||
|
||||
const expiresIn = expiresInToSeconds(formSetupKey.expiresInFormatted);
|
||||
const expiresIn = formSetupKey.expires_in
|
||||
? formSetupKey.expires_in * 24 * 3600
|
||||
: 7 * 24 * 3600; // the api expects seconds we have days
|
||||
return {
|
||||
id: formSetupKey.id,
|
||||
name: formSetupKey.name,
|
||||
type: formSetupKey.type,
|
||||
auto_groups: autoGroups,
|
||||
type: formSetupKey.type ? formSetupKey.type : "one-off",
|
||||
auto_groups: existingGroups,
|
||||
revoked: formSetupKey.revoked,
|
||||
groupsToCreate: groupsToCreate,
|
||||
expires_in: expiresIn,
|
||||
@@ -185,7 +149,7 @@ const SetupKeyNew = () => {
|
||||
type: "one-off",
|
||||
key: "",
|
||||
last_used: "",
|
||||
expires: "",
|
||||
expires: "7",
|
||||
state: "valid",
|
||||
auto_groups: [] as string[],
|
||||
usage_limit: 0,
|
||||
@@ -227,26 +191,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}
|
||||
@@ -276,25 +220,27 @@ const SetupKeyNew = () => {
|
||||
);
|
||||
|
||||
const changesDetected = (): boolean => {
|
||||
console.log("formSetupKey", formSetupKey);
|
||||
return (
|
||||
formSetupKey.name == null ||
|
||||
formSetupKey.name !== setupKey.name ||
|
||||
groupsChanged() ||
|
||||
formSetupKey.usage_limit !== setupKey.usage_limit
|
||||
formSetupKey?.usage_limit !== setupKey.usage_limit
|
||||
);
|
||||
};
|
||||
|
||||
const groupsChanged = (): boolean => {
|
||||
console.log("groupsChanged", formSetupKey);
|
||||
if (
|
||||
setupKey &&
|
||||
setupKey.auto_groups &&
|
||||
formSetupKey.autoGroupNames.length !== setupKey.auto_groups.length
|
||||
setupKey?.auto_groups &&
|
||||
formSetupKey?.autoGroupNames &&
|
||||
formSetupKey?.autoGroupNames.length !== setupKey?.auto_groups.length
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const formGroupIds =
|
||||
groups
|
||||
?.filter((g) => formSetupKey.autoGroupNames.includes(g.name))
|
||||
?.filter((g) => formSetupKey?.autoGroupNames.includes(g.name))
|
||||
.map((g) => g.id || "") || [];
|
||||
|
||||
return (
|
||||
@@ -339,7 +285,7 @@ const SetupKeyNew = () => {
|
||||
fontSize: "14px",
|
||||
borderRadius: "2px",
|
||||
}}
|
||||
disabled={savedSetupKey.loading || !changesDetected()}
|
||||
disabled={savedSetupKey.loading || !!!formSetupKey.name}
|
||||
onClick={handleFormSubmit}
|
||||
>
|
||||
Create key
|
||||
@@ -369,8 +315,8 @@ const SetupKeyNew = () => {
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
fontSize: "22px",
|
||||
margin: "0px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
{showPlainToken
|
||||
@@ -382,7 +328,10 @@ const SetupKeyNew = () => {
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
paddingBottom: "15px",
|
||||
marginTop: "-20px",
|
||||
fontSize: "14px",
|
||||
paddingBottom: "25px",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
{showPlainToken
|
||||
@@ -396,14 +345,17 @@ const SetupKeyNew = () => {
|
||||
form={form}
|
||||
onValuesChange={onChange}
|
||||
initialValues={{
|
||||
expiresIn: ExpiresInDefault,
|
||||
usage_limit: 1,
|
||||
expires_in: "7",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Paragraph style={{ fontWeight: "bold" }}>Name</Paragraph>
|
||||
<Paragraph type={"secondary"} style={{ marginTop: "-15px" }}>
|
||||
<Paragraph style={{ fontWeight: "500" }}>Name</Paragraph>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{ marginTop: "-15px", marginBottom: "-5px" }}
|
||||
>
|
||||
Set an easily identifiable name for your key
|
||||
</Paragraph>
|
||||
</Col>
|
||||
@@ -421,13 +373,13 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "20px" }} justify={"space-between"}>
|
||||
<Row style={{ marginTop: "24px" }} justify={"space-between"}>
|
||||
<Col span={18}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Reusable
|
||||
@@ -443,6 +395,7 @@ const SetupKeyNew = () => {
|
||||
<Row justify={"end"}>
|
||||
<Form.Item name="reusable" valuePropName="checked">
|
||||
<Switch
|
||||
size="small"
|
||||
onChange={(checked) => {
|
||||
setFormSetupKey({
|
||||
...formSetupKey,
|
||||
@@ -455,13 +408,13 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "10px" }}>
|
||||
<Row style={{ marginTop: "16px" }}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Usage limit
|
||||
@@ -493,13 +446,13 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "20px" }}>
|
||||
<Row style={{ marginTop: "24px" }}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Expires in
|
||||
@@ -507,13 +460,19 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
name="expiresIn"
|
||||
name="expires_in"
|
||||
rules={[
|
||||
{
|
||||
type: "number",
|
||||
min: 1,
|
||||
max: 365,
|
||||
message:
|
||||
"The expiration should be set between 1 and 365 days",
|
||||
},
|
||||
{ required: true, message: "Please enter expiration date" },
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
defaultValue={7}
|
||||
placeholder={`2`}
|
||||
type="number"
|
||||
addonAfter=" Days"
|
||||
@@ -524,17 +483,18 @@ const SetupKeyNew = () => {
|
||||
type={"secondary"}
|
||||
style={{ marginTop: "-18px", marginBottom: 0 }}
|
||||
>
|
||||
Should be between 1 and 180 days
|
||||
Should be between 1 and 365 days
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{ marginTop: "20px" }}>
|
||||
<Row style={{ marginTop: "24px" }}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Auto-assigned groups
|
||||
@@ -548,25 +508,27 @@ const SetupKeyNew = () => {
|
||||
<Form.Item
|
||||
style={{ marginTop: "5px", marginBottom: 0 }}
|
||||
name="autoGroupNames"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
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)}
|
||||
optionFilterProp="serchValue"
|
||||
>
|
||||
{tagGroups.map((m) => (
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{ marginTop: "40px", marginBottom: "28px" }}>
|
||||
<Row style={{ marginTop: "24px", marginBottom: "24px" }}>
|
||||
<Text type={"secondary"}>
|
||||
Learn more about
|
||||
<a
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Modal } from "antd";
|
||||
import PeerUpdate from "./PeerUpdate";
|
||||
export const UpdatePeerGroupModal = (props:any) => {
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
closable={false}
|
||||
open={true}
|
||||
footer={[]}
|
||||
onCancel={() => props.setShowGroupModal(false)}
|
||||
width={450}
|
||||
>
|
||||
<PeerUpdate
|
||||
isGroupUpdateView={true}
|
||||
setShowGroupModal={props.setShowGroupModal}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+652
-614
File diff suppressed because it is too large.
Load diff
Loaded 20 of 64 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user