mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Re-work DNS layout (#222)
This commit is contained in:
908
src/components/NameServerGroupAdd.tsx
Normal file
908
src/components/NameServerGroupAdd.tsx
Normal file
@@ -0,0 +1,908 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import { actions as nsGroupActions } from "../store/nameservers";
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Switch,
|
||||
Form,
|
||||
FormListFieldData,
|
||||
Input,
|
||||
InputNumber,
|
||||
message,
|
||||
Modal,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {
|
||||
CloseOutlined,
|
||||
MinusCircleOutlined,
|
||||
PlusOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Header } from "antd/es/layout/layout";
|
||||
import { RuleObject } from "antd/lib/form";
|
||||
import cidrRegex from "cidr-regex";
|
||||
import {
|
||||
NameServer,
|
||||
NameServerGroup,
|
||||
NameServerGroupToSave,
|
||||
} from "../store/nameservers/types";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
|
||||
const { Paragraph, Text } = Typography;
|
||||
|
||||
interface formNSGroup extends NameServerGroup {}
|
||||
|
||||
const NameServerGroupAdd = () => {
|
||||
const {
|
||||
blueTagRender,
|
||||
handleChangeTags,
|
||||
dropDownRender,
|
||||
optionRender,
|
||||
tagGroups,
|
||||
getExistingAndToCreateGroupsLists,
|
||||
getGroupNamesFromIDs,
|
||||
selectValidator,
|
||||
} = useGetGroupTagHelpers();
|
||||
const dispatch = useDispatch();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const { Option } = Select;
|
||||
const nsGroup = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.nameserverGroup
|
||||
);
|
||||
const setupNewNameServerGroupVisible = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.setupNewNameServerGroupVisible
|
||||
);
|
||||
const savedNSGroup = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.savedNameServerGroup
|
||||
);
|
||||
const nsGroupData = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.data
|
||||
);
|
||||
|
||||
const [formNSGroup, setFormNSGroup] = useState({} as formNSGroup);
|
||||
const [form] = Form.useForm();
|
||||
const [editName, setEditName] = useState(false);
|
||||
const [isPrimary, setIsPrimary] = useState(false);
|
||||
const [editDescription, setEditDescription] = useState(false);
|
||||
const inputNameRef = useRef<any>(null);
|
||||
const inputDescriptionRef = useRef<any>(null);
|
||||
const [selectCustom, setSelectCustom] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (editName)
|
||||
inputNameRef.current!.focus({
|
||||
cursor: "end",
|
||||
});
|
||||
}, [editName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editDescription)
|
||||
inputDescriptionRef.current!.focus({
|
||||
cursor: "end",
|
||||
});
|
||||
}, [editDescription]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!nsGroup) return;
|
||||
|
||||
let newFormGroup = {
|
||||
...nsGroup,
|
||||
groups: getGroupNamesFromIDs(nsGroup.groups),
|
||||
} as formNSGroup;
|
||||
setFormNSGroup(newFormGroup);
|
||||
form.setFieldsValue(newFormGroup);
|
||||
if (nsGroup.id) {
|
||||
setSelectCustom(true);
|
||||
}
|
||||
if (nsGroup.primary !== undefined) {
|
||||
setIsPrimary(nsGroup.primary);
|
||||
}
|
||||
}, [nsGroup]);
|
||||
|
||||
const onCancel = () => {
|
||||
dispatch(nsGroupActions.setSetupNewNameServerGroupVisible(false));
|
||||
dispatch(
|
||||
nsGroupActions.setNameServerGroup({
|
||||
id: "",
|
||||
name: "",
|
||||
description: "",
|
||||
primary: false,
|
||||
domains: [],
|
||||
nameservers: [] as NameServer[],
|
||||
groups: [],
|
||||
enabled: false,
|
||||
} as NameServerGroup)
|
||||
);
|
||||
setEditName(false);
|
||||
setSelectCustom(false);
|
||||
setIsPrimary(false);
|
||||
};
|
||||
|
||||
const onChange = (changedValues: any) => {
|
||||
if (changedValues.primary !== undefined) {
|
||||
setIsPrimary(changedValues.primary);
|
||||
}
|
||||
setFormNSGroup({ ...formNSGroup, ...changedValues });
|
||||
};
|
||||
|
||||
let googleChoice = "Google DNS";
|
||||
let cloudflareChoice = "Cloudflare DNS";
|
||||
let quad9Choice = "Quad9 DNS";
|
||||
let customChoice = "Add custom nameserver";
|
||||
|
||||
let defaultDNSOptions: NameServerGroup[] = [
|
||||
{
|
||||
name: googleChoice,
|
||||
description: "Google DNS servers",
|
||||
domains: [],
|
||||
primary: true,
|
||||
nameservers: [
|
||||
{
|
||||
ip: "8.8.8.8",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
{
|
||||
ip: "8.8.4.4",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
],
|
||||
groups: [],
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: cloudflareChoice,
|
||||
description: "Cloudflare DNS servers",
|
||||
domains: [],
|
||||
primary: true,
|
||||
nameservers: [
|
||||
{
|
||||
ip: "1.1.1.1",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
{
|
||||
ip: "1.0.0.1",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
],
|
||||
groups: [],
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: quad9Choice,
|
||||
description: "Quad9 DNS servers",
|
||||
domains: [],
|
||||
primary: true,
|
||||
nameservers: [
|
||||
{
|
||||
ip: "9.9.9.9",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
{
|
||||
ip: "149.112.112.112",
|
||||
ns_type: "udp",
|
||||
port: 53,
|
||||
},
|
||||
],
|
||||
groups: [],
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
const handleSelectChange = (value: string) => {
|
||||
let nsGroupLocal = {} as NameServerGroup;
|
||||
if (value === customChoice) {
|
||||
nsGroupLocal = nsGroup;
|
||||
} else {
|
||||
defaultDNSOptions.forEach((nsg) => {
|
||||
if (value === nsg.name) {
|
||||
nsGroupLocal = nsg;
|
||||
}
|
||||
});
|
||||
}
|
||||
let newFormGroup = {
|
||||
...nsGroupLocal,
|
||||
groups: getGroupNamesFromIDs(nsGroupLocal.groups),
|
||||
} as formNSGroup;
|
||||
setFormNSGroup(newFormGroup);
|
||||
form.setFieldsValue(newFormGroup);
|
||||
setSelectCustom(true);
|
||||
};
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
const nsGroupToSave = createNSGroupToSave(values as NameServerGroup);
|
||||
dispatch(
|
||||
nsGroupActions.saveNameServerGroup.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: nsGroupToSave,
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(() => onCancel())
|
||||
.catch((errorInfo) => {
|
||||
let msg = "please check the fields and try again";
|
||||
if (errorInfo.errorFields) {
|
||||
msg = errorInfo.errorFields[0].errors[0];
|
||||
}
|
||||
message.error({
|
||||
content: msg,
|
||||
duration: 1,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const createNSGroupToSave = (
|
||||
values: NameServerGroup
|
||||
): NameServerGroupToSave => {
|
||||
let [existingGroups, newGroups] = getExistingAndToCreateGroupsLists(
|
||||
values.groups
|
||||
);
|
||||
return {
|
||||
id: formNSGroup.id || null,
|
||||
name: values.name ? values.name : formNSGroup.name,
|
||||
description: values.description
|
||||
? values.description
|
||||
: formNSGroup.description,
|
||||
primary: values.domains.length ? false : true,
|
||||
domains: values.primary ? [] : values.domains,
|
||||
nameservers: values.nameservers,
|
||||
groups: existingGroups,
|
||||
groupsToCreate: newGroups,
|
||||
enabled: values.enabled,
|
||||
} as NameServerGroupToSave;
|
||||
};
|
||||
|
||||
const toggleEditName = (status: boolean) => {
|
||||
setEditName(status);
|
||||
};
|
||||
|
||||
const toggleEditDescription = (status: boolean) => {
|
||||
setEditDescription(status);
|
||||
};
|
||||
|
||||
const domainRegex =
|
||||
/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/;
|
||||
|
||||
const domainValidator = (_: RuleObject, domain: string) => {
|
||||
if (domainRegex.test(domain)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
setIsPrimary(false);
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
"Please enter a valid domain, e.g. example.com or intra.example.com"
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const nameValidator = (_: RuleObject, value: string) => {
|
||||
const found = nsGroupData.find(
|
||||
(u) => u.name == value && u.id !== formNSGroup.id
|
||||
);
|
||||
if (found) {
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
"Please enter a unique name for your nameserver configuration"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const ipValidator = (_: RuleObject, value: string) => {
|
||||
if (!cidrRegex().test(value + "/32")) {
|
||||
return Promise.reject(
|
||||
new Error("Please enter a valid IP, e.g. 192.168.1.1 or 8.8.8.8")
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const formListValidator = (_: RuleObject, names) => {
|
||||
if (names.length >= 3) {
|
||||
return Promise.reject(
|
||||
new Error("Exceeded maximum number of Nameservers. (Max is 2)")
|
||||
);
|
||||
}
|
||||
if (names.length < 1) {
|
||||
return Promise.reject(new Error("You should add at least 1 Nameserver"));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderNSList = (
|
||||
fields: FormListFieldData[],
|
||||
{ add, remove }: any,
|
||||
{ errors }: any
|
||||
) => (
|
||||
<Col>
|
||||
<div style={{ width: "100%", maxWidth: "360px" }}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
marginBottom: "10px",
|
||||
display: "block",
|
||||
}}
|
||||
>
|
||||
Nameservers
|
||||
</label>
|
||||
|
||||
{!!fields.length && (
|
||||
<Row align="middle">
|
||||
<Col span={4} style={{ textAlign: "left" }}>
|
||||
<Typography.Text
|
||||
style={{ color: "#818183", paddingLeft: "5px" }}
|
||||
></Typography.Text>
|
||||
</Col>
|
||||
<Col span={10} style={{ textAlign: "left" }}>
|
||||
<Typography.Text style={{ color: "#818183", paddingLeft: "5px" }}>
|
||||
Nameserver IP
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={4} style={{ textAlign: "left" }}>
|
||||
<Typography.Text style={{ color: "#818183", paddingLeft: "5px" }}>
|
||||
Port
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={4} />
|
||||
</Row>
|
||||
)}
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<Row key={index}>
|
||||
<Col span={4} style={{ textAlign: "left" }}>
|
||||
<Form.Item
|
||||
style={{ margin: "3px" }}
|
||||
name={[field.name, "ns_type"]}
|
||||
rules={[
|
||||
{ required: true, message: "Missing first protocol" },
|
||||
]}
|
||||
initialValue={"udp"}
|
||||
>
|
||||
<Select
|
||||
disabled
|
||||
style={{ width: "100%" }}
|
||||
className="style-like-text"
|
||||
>
|
||||
<Option value="udp">UDP</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={10} style={{ margin: "1px" }}>
|
||||
<Form.Item
|
||||
style={{ margin: "1px" }}
|
||||
name={[field.name, "ip"]}
|
||||
rules={[{ validator: ipValidator }]}
|
||||
>
|
||||
<Input
|
||||
placeholder="e.g. X.X.X.X"
|
||||
style={{ width: "100%" }}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4} style={{ textAlign: "center" }}>
|
||||
<Form.Item
|
||||
style={{ margin: "1px" }}
|
||||
name={[field.name, "port"]}
|
||||
rules={[{ required: true, message: "Missing port" }]}
|
||||
initialValue={53}
|
||||
>
|
||||
<InputNumber placeholder="Port" style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col
|
||||
span={2}
|
||||
style={{
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<MinusCircleOutlined onClick={() => remove(field.name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
|
||||
<Row>
|
||||
<Col span={20}>
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
style={{
|
||||
maxWidth: "270px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
disabled={fields.length > 1}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Add Nameserver
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const renderDomains = (
|
||||
fields: FormListFieldData[],
|
||||
{ add, remove }: any,
|
||||
{ errors }: any
|
||||
) => (
|
||||
<>
|
||||
<Row>
|
||||
<Space>
|
||||
<Col>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Match domains
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "0",
|
||||
}}
|
||||
>
|
||||
Add domain if you want to have a specific one
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Space>
|
||||
</Row>
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<Row key={index} style={{ marginBottom: "5px" }}>
|
||||
<Col span={22}>
|
||||
<Form.Item
|
||||
style={{ margin: "0" }}
|
||||
{...field}
|
||||
rules={[{ validator: domainValidator }]}
|
||||
>
|
||||
<Input
|
||||
placeholder="e.g. example.com"
|
||||
style={{ width: "100%" }}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col
|
||||
span={2}
|
||||
style={{
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<MinusCircleOutlined
|
||||
className="dynamic-delete-button"
|
||||
onClick={() => remove(field.name)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
|
||||
<Row>
|
||||
<Col span={24} style={{ margin: "1px" }}>
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
style={{ marginTop: "5px" }}
|
||||
>
|
||||
Add Domain
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</>
|
||||
);
|
||||
|
||||
const handleChangeDisabled = (checked: boolean) => {
|
||||
setFormNSGroup({
|
||||
...formNSGroup,
|
||||
enabled: checked,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{nsGroup && (
|
||||
<Modal
|
||||
forceRender={true}
|
||||
footer={false}
|
||||
onCancel={onCancel}
|
||||
open={setupNewNameServerGroupVisible}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
fontSize: "18px",
|
||||
margin: "0px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Add Nameserver
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
paddingBottom: "0",
|
||||
}}
|
||||
>
|
||||
Use this nameserver to resolve domains in your network
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
{selectCustom ? (
|
||||
<Form
|
||||
layout="vertical"
|
||||
requiredMark={false}
|
||||
form={form}
|
||||
onValuesChange={onChange}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<Header
|
||||
style={{
|
||||
border: "none",
|
||||
}}
|
||||
>
|
||||
<Row align="top">
|
||||
<Col flex="none" style={{ display: "flex" }}>
|
||||
{!editName && !editDescription && formNSGroup.id && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
className="ant-drawer-close"
|
||||
style={{ paddingTop: 3 }}
|
||||
onClick={onCancel}
|
||||
>
|
||||
<span
|
||||
role="img"
|
||||
aria-label="close"
|
||||
className="anticon anticon-close"
|
||||
>
|
||||
<CloseOutlined size={16} />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</Col>
|
||||
<Col flex="auto">
|
||||
{!editName && formNSGroup.id ? (
|
||||
<div
|
||||
className={
|
||||
"access-control input-text ant-drawer-title"
|
||||
}
|
||||
onClick={() => toggleEditName(true)}
|
||||
>
|
||||
{formNSGroup.id
|
||||
? formNSGroup.name
|
||||
: "New nameserver group"}
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ lineHeight: "15px" }}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
<Form.Item
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message:
|
||||
"Please add an identifier for this nameserver group",
|
||||
whitespace: true,
|
||||
},
|
||||
{
|
||||
validator: nameValidator,
|
||||
},
|
||||
]}
|
||||
style={{
|
||||
marginBottom: "10px",
|
||||
marginTop: "10px",
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
placeholder="e.g. Public DNS"
|
||||
ref={inputNameRef}
|
||||
onPressEnter={() => toggleEditName(false)}
|
||||
onBlur={() => toggleEditName(false)}
|
||||
autoComplete="off"
|
||||
maxLength={40}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
{!editDescription ? (
|
||||
<div
|
||||
className={
|
||||
"access-control input-text ant-drawer-subtitle"
|
||||
}
|
||||
style={{ marginTop: "0" }}
|
||||
onClick={() => toggleEditDescription(true)}
|
||||
>
|
||||
{formNSGroup.description &&
|
||||
formNSGroup.description.trim() !== ""
|
||||
? formNSGroup.description
|
||||
: "Add description"}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{ lineHeight: "15px", marginTop: "24px" }}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Description
|
||||
</label>
|
||||
<Form.Item
|
||||
name="description"
|
||||
style={{ marginTop: "8px" }}
|
||||
>
|
||||
<Input
|
||||
placeholder="Add description..."
|
||||
ref={inputDescriptionRef}
|
||||
onPressEnter={() =>
|
||||
toggleEditDescription(false)
|
||||
}
|
||||
onBlur={() => toggleEditDescription(false)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Header>
|
||||
</Col>
|
||||
<Col span={24} flex="auto">
|
||||
<Form.List
|
||||
name="nameservers"
|
||||
rules={[{ validator: formListValidator }]}
|
||||
>
|
||||
{renderNSList}
|
||||
</Form.List>
|
||||
</Col>
|
||||
<Col span={24} flex="auto">
|
||||
<Form.List name="domains">{renderDomains}</Form.List>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Distribution groups
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "0",
|
||||
}}
|
||||
>
|
||||
Advertise this route to peers that belong to the following
|
||||
groups
|
||||
</Paragraph>
|
||||
<Form.Item
|
||||
name="groups"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Associate groups with the NS group"
|
||||
tagRender={blueTagRender}
|
||||
onChange={handleChangeTags}
|
||||
dropdownRender={dropDownRender}
|
||||
>
|
||||
{tagGroups.map((m) => (
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item name="enabled" label="">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
onChange={handleChangeDisabled}
|
||||
defaultChecked={formNSGroup.enabled}
|
||||
size="small"
|
||||
/>
|
||||
<div>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Enabled
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "0",
|
||||
}}
|
||||
>
|
||||
Disable this server if you don't want it to apply
|
||||
immediately
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col
|
||||
span={24}
|
||||
style={{ marginTop: "10px", marginBottom: "20px" }}
|
||||
>
|
||||
<Text type={"secondary"}>
|
||||
Learn more about
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
DNS
|
||||
</a>
|
||||
</Text>
|
||||
</Col>
|
||||
<Col
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Space
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "end",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Button onClick={onCancel} disabled={savedNSGroup.loading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleFormSubmit}
|
||||
disabled={savedNSGroup.loading}
|
||||
>
|
||||
Create Nameserver
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
) : (
|
||||
<>
|
||||
<Space direction={"vertical"} style={{ width: "100%" }}>
|
||||
<Row align="middle">
|
||||
<Col span={24} style={{ textAlign: "left" }}>
|
||||
<span className="ant-form-item font-500">
|
||||
Select a predefined one
|
||||
</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row align="middle">
|
||||
<Col span={24} style={{ textAlign: "center" }}>
|
||||
<Select
|
||||
style={{ width: "100%" }}
|
||||
onChange={handleSelectChange}
|
||||
options={[
|
||||
{
|
||||
value: googleChoice,
|
||||
label: googleChoice,
|
||||
},
|
||||
{
|
||||
value: cloudflareChoice,
|
||||
label: cloudflareChoice,
|
||||
},
|
||||
{
|
||||
value: quad9Choice,
|
||||
label: quad9Choice,
|
||||
},
|
||||
{
|
||||
value: customChoice,
|
||||
label: customChoice,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row align="middle">
|
||||
<Col span={24} style={{ textAlign: "left" }}>
|
||||
<Col span={24} style={{ textAlign: "left" }}>
|
||||
<span className="ant-form-item blue-color">
|
||||
<Typography.Link
|
||||
onClick={() => handleSelectChange(customChoice)}
|
||||
>
|
||||
or create custom
|
||||
</Typography.Link>
|
||||
</span>
|
||||
</Col>
|
||||
</Col>
|
||||
</Row>
|
||||
</Space>
|
||||
<Space
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "end",
|
||||
marginTop: "25px",
|
||||
}}
|
||||
>
|
||||
<Button onClick={onCancel} type="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NameServerGroupAdd;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -366,7 +366,6 @@ const PeerUpdate = () => {
|
||||
noUpdateToName() &&
|
||||
noUpdateToLoginExpiration()
|
||||
) {
|
||||
console.log("no group update==<");
|
||||
const style = { marginTop: 85 };
|
||||
if (savedGroups.loading) {
|
||||
message.loading({
|
||||
|
||||
@@ -234,7 +234,6 @@ const RoutePeerUpdate = () => {
|
||||
style: styleNotification,
|
||||
});
|
||||
} else if (savedRoute.success) {
|
||||
console.log("savedRoute", savedRoute);
|
||||
message.success({
|
||||
content: "Route has been successfully updated.",
|
||||
key: saveKey,
|
||||
|
||||
@@ -259,7 +259,6 @@ const RouteAddNew = () => {
|
||||
style: styleNotification,
|
||||
});
|
||||
} else if (savedRoute.success) {
|
||||
console.log("savedRoute", savedRoute);
|
||||
message.success({
|
||||
content: "Route has been successfully added.",
|
||||
key: saveKey,
|
||||
|
||||
@@ -211,6 +211,9 @@ td.non-highlighted-table-column {
|
||||
|
||||
.ant-form-item-explain-error {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.menlo-font,
|
||||
@@ -441,4 +444,27 @@ ul.ant-list-items {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.blue-color {
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
.style-like-text .ant-select-selector {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
color: rgba(0, 0, 0, 1) !important;
|
||||
}
|
||||
|
||||
.style-like-text .ant-select-selection-search::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.style-like-text .ant-select-selection-item {
|
||||
padding-inline-end: 0 !important;
|
||||
}
|
||||
|
||||
.style-like-text .ant-select-arrow {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ const actions = {
|
||||
|
||||
setNameServerGroup: createAction('SET_NameServerGroup')<NameServerGroup>(),
|
||||
setSetupNewNameServerGroupVisible: createAction('SET_SETUP_NEW_NameServerGroup_VISIBLE')<boolean>(),
|
||||
setSetupEditNameServerGroupVisible: createAction('SET_SETUP_EDIT_NameServerGroup_VISIBLE')<boolean>(),
|
||||
setSetupNewNameServerGroupHA: createAction('SET_SETUP_NEW_NameServerGroup_HA')<boolean>()
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ type StateType = Readonly<{
|
||||
deleteNameServerGroup: DeleteResponse<string | null>;
|
||||
savedNameServerGroup: CreateResponse<NameServerGroup | null>;
|
||||
setupNewNameServerGroupVisible: boolean;
|
||||
setupEditNameServerGroupVisible: boolean;
|
||||
setupNewNameServerGroupHA: boolean
|
||||
}>;
|
||||
|
||||
@@ -27,17 +28,18 @@ const initialState: StateType = {
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data : null
|
||||
data: null,
|
||||
},
|
||||
savedNameServerGroup: <CreateResponse<NameServerGroup | null>>{
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data : null
|
||||
data: null,
|
||||
},
|
||||
setupNewNameServerGroupVisible: false,
|
||||
setupNewNameServerGroupHA: false
|
||||
setupEditNameServerGroupVisible: false,
|
||||
setupNewNameServerGroupHA: false,
|
||||
};
|
||||
|
||||
const data = createReducer<NameServerGroup[], ActionTypes>(initialState.data as NameServerGroup[])
|
||||
@@ -79,6 +81,9 @@ const savedNameServerGroup = createReducer<CreateResponse<NameServerGroup | null
|
||||
const setupNewNameServerGroupVisible = createReducer<boolean, ActionTypes>(initialState.setupNewNameServerGroupVisible)
|
||||
.handleAction(actions.setSetupNewNameServerGroupVisible, (store, action) => action.payload)
|
||||
|
||||
const setupEditNameServerGroupVisible = createReducer<boolean, ActionTypes>(initialState.setupEditNameServerGroupVisible)
|
||||
.handleAction(actions.setSetupEditNameServerGroupVisible, (store, action) => action.payload)
|
||||
|
||||
const setupNewNameServerGroupHA = createReducer<boolean, ActionTypes>(initialState.setupNewNameServerGroupHA)
|
||||
.handleAction(actions.setSetupNewNameServerGroupHA, (store, action) => action.payload)
|
||||
|
||||
@@ -91,5 +96,6 @@ export default combineReducers({
|
||||
deletedNameServerGroup,
|
||||
savedNameServerGroup,
|
||||
setupNewNameServerGroupVisible,
|
||||
setupNewNameServerGroupHA
|
||||
setupEditNameServerGroupVisible,
|
||||
setupNewNameServerGroupHA,
|
||||
});
|
||||
|
||||
@@ -118,13 +118,13 @@ export function* setDeleteNameServerGroup(action: ReturnType<typeof actions.set
|
||||
|
||||
export function* deleteNameServerGroup(action: ReturnType<typeof actions.deleteNameServerGroup.request>): Generator {
|
||||
try {
|
||||
yield call(actions.setDeletedNameServerGroup,{
|
||||
yield put(actions.setDeletedNameServerGroup({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>)
|
||||
}))
|
||||
|
||||
const effect = yield call(service.deletedNameServerGroup, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
export interface NameServerGroup {
|
||||
id?: string
|
||||
name: string
|
||||
description: string
|
||||
primary: boolean
|
||||
domains: string[]
|
||||
nameservers: NameServer[]
|
||||
groups: string[]
|
||||
enabled: boolean
|
||||
id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
primary: boolean;
|
||||
domains: string[];
|
||||
nameservers: NameServer[];
|
||||
groups: string[];
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface NameServer {
|
||||
ip: string
|
||||
ns_type: string
|
||||
port: number
|
||||
ip: string;
|
||||
ns_type: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export interface NameServerGroupToSave extends NameServerGroup
|
||||
{
|
||||
groupsToCreate: string[]
|
||||
}
|
||||
export interface NameServerGroupToSave extends NameServerGroup {
|
||||
groupsToCreate?: string[];
|
||||
}
|
||||
|
||||
@@ -174,13 +174,15 @@ export function* deletePolicy(
|
||||
action: ReturnType<typeof actions.deletePolicy.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield call(actions.setDeletedPolicy, {
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>);
|
||||
yield put(
|
||||
actions.setDeletedPolicy({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
})
|
||||
);
|
||||
|
||||
const effect = yield call(service.deletedPolicy, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
|
||||
@@ -549,7 +549,6 @@ export const AccessControl = () => {
|
||||
})
|
||||
);
|
||||
};
|
||||
console.log("dataTable", dataTable);
|
||||
return (
|
||||
<>
|
||||
{!setupEditPolicyVisible && (
|
||||
|
||||
@@ -1,78 +1,91 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Container} from "../components/Container";
|
||||
import {
|
||||
Col,
|
||||
Row,
|
||||
Tabs,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import type { TabsProps } from 'antd';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Container } from "../components/Container";
|
||||
import { Col, Row, Tabs, Typography } from "antd";
|
||||
import type { TabsProps } from "antd";
|
||||
import NameServerGroupUpdate from "../components/NameServerGroupUpdate";
|
||||
import NameServerGroupAdd from "../components/NameServerGroupAdd";
|
||||
import Nameservers from "./Nameservers";
|
||||
import {actions as groupActions} from "../store/group";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import { actions as groupActions } from "../store/group";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import DNSSettingsForm from "./DNSSettings";
|
||||
import {RootState} from "typesafe-actions";
|
||||
import {actions as dnsSettingsActions} from '../store/dns-settings';
|
||||
import {useGetGroupTagHelpers} from "../utils/groups";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import { actions as dnsSettingsActions } from "../store/dns-settings";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
|
||||
const {Title, Paragraph} = Typography;
|
||||
const { Title, Paragraph } = Typography;
|
||||
|
||||
export const DNS = () => {
|
||||
const {getTokenSilently} = useGetTokenSilently()
|
||||
const dispatch = useDispatch()
|
||||
const {
|
||||
getGroupNamesFromIDs,
|
||||
} = useGetGroupTagHelpers()
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
const { getGroupNamesFromIDs } = useGetGroupTagHelpers();
|
||||
|
||||
const dnsSettingsData = useSelector((state: RootState) => state.dnsSettings.data)
|
||||
const dnsSettingsData = useSelector(
|
||||
(state: RootState) => state.dnsSettings.data
|
||||
);
|
||||
const setupEditNameServerGroupVisible = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.setupEditNameServerGroupVisible
|
||||
);
|
||||
const setupNewNameServerGroupVisible = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.setupNewNameServerGroupVisible
|
||||
);
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
groupActions.getGroups.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(groupActions.getGroups.request({getAccessTokenSilently: getTokenSilently, payload: null}));
|
||||
}, [])
|
||||
const nsTabKey = "1";
|
||||
const items: TabsProps["items"] = [
|
||||
{
|
||||
key: nsTabKey,
|
||||
label: "Nameservers",
|
||||
children: <Nameservers />,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
label: "Settings",
|
||||
children: <DNSSettingsForm />,
|
||||
},
|
||||
];
|
||||
|
||||
const nsTabKey = '1'
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: nsTabKey,
|
||||
label: 'Nameservers',
|
||||
children: <Nameservers/>,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Settings',
|
||||
children: <DNSSettingsForm/>,
|
||||
},
|
||||
]
|
||||
|
||||
const onTabClick = (key:string) => {
|
||||
if (key == nsTabKey) {
|
||||
if (!dnsSettingsData) return
|
||||
dispatch(dnsSettingsActions.setDNSSettings({
|
||||
disabled_management_groups: getGroupNamesFromIDs(dnsSettingsData.disabled_management_groups),
|
||||
}))
|
||||
}
|
||||
const onTabClick = (key: string) => {
|
||||
if (key == nsTabKey) {
|
||||
if (!dnsSettingsData) return;
|
||||
dispatch(
|
||||
dnsSettingsActions.setDNSSettings({
|
||||
disabled_management_groups: getGroupNamesFromIDs(
|
||||
dnsSettingsData.disabled_management_groups
|
||||
),
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container style={{paddingTop: "40px"}}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Tabs
|
||||
defaultActiveKey={nsTabKey}
|
||||
items={items}
|
||||
onTabClick={onTabClick}
|
||||
animated={{ inkBar: true, tabPane: false }}
|
||||
tabPosition="top"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<NameServerGroupUpdate/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{!setupEditNameServerGroupVisible && (
|
||||
<Container style={{ paddingTop: "40px" }}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Tabs
|
||||
defaultActiveKey={nsTabKey}
|
||||
items={items}
|
||||
onTabClick={onTabClick}
|
||||
animated={{ inkBar: true, tabPane: false }}
|
||||
tabPosition="top"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)}
|
||||
{setupEditNameServerGroupVisible && <NameServerGroupUpdate />}
|
||||
{setupNewNameServerGroupVisible && <NameServerGroupAdd />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DNS;
|
||||
export default DNS;
|
||||
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Dropdown,
|
||||
Input,
|
||||
Menu,
|
||||
Switch,
|
||||
message,
|
||||
Modal,
|
||||
Popover,
|
||||
@@ -30,8 +29,7 @@ import { actions as groupActions } from "../store/group";
|
||||
import { Group } from "../store/group/types";
|
||||
import { TooltipPlacement } from "antd/es/tooltip";
|
||||
import { NameServer, NameServerGroup } from "../store/nameservers/types";
|
||||
import NameServerGroupUpdate from "../components/NameServerGroupUpdate";
|
||||
import { EllipsisOutlined, ExclamationCircleOutlined } from "@ant-design/icons";
|
||||
import { ExclamationCircleOutlined } from "@ant-design/icons";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
import { usePageSizeHelpers } from "../utils/pageSize";
|
||||
|
||||
@@ -60,30 +58,35 @@ export const Nameservers = () => {
|
||||
const loading = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.loading
|
||||
);
|
||||
const updateNameServerGroupVisible = useSelector(
|
||||
const addNewNameServerGroupVisible = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.setupNewNameServerGroupVisible
|
||||
);
|
||||
const savedNSGroup = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.savedNameServerGroup
|
||||
);
|
||||
|
||||
const deleteNSGroup = useSelector(
|
||||
(state: RootState) => state.nameserverGroup.deletedNameServerGroup
|
||||
);
|
||||
|
||||
const [groupPopupVisible, setGroupPopupVisible] = useState("");
|
||||
const [nsGroupToAction, setNsGroupToAction] = useState(
|
||||
null as NameserverGroupDataTable | null
|
||||
);
|
||||
|
||||
const [textToSearch, setTextToSearch] = useState("");
|
||||
const [optionAllEnable, setOptionAllEnable] = useState("enabled");
|
||||
const [optionAllEnable, setOptionAllEnable] = useState("all");
|
||||
const [dataTable, setDataTable] = useState([] as NameserverGroupDataTable[]);
|
||||
const [showTutorial, setShowTutorial] = useState(false);
|
||||
|
||||
const optionsAllEnabled = [
|
||||
{ label: "Enabled", value: "enabled" },
|
||||
{ label: "All", value: "all" },
|
||||
{ label: "Enabled", value: "enabled" },
|
||||
];
|
||||
|
||||
// setUserAndView makes the UserUpdate drawer visible (right side) and sets the user object
|
||||
const setUserAndView = (nsGroup: NameServerGroup) => {
|
||||
dispatch(nsGroupActions.setSetupNewNameServerGroupVisible(true));
|
||||
dispatch(nsGroupActions.setSetupEditNameServerGroupVisible(true));
|
||||
dispatch(
|
||||
nsGroupActions.setNameServerGroup({
|
||||
id: nsGroup.id,
|
||||
@@ -166,24 +169,9 @@ export const Nameservers = () => {
|
||||
setDataTable(transformDataTable(filterDataTable()));
|
||||
};
|
||||
|
||||
const onClickEdit = () => {
|
||||
dispatch(nsGroupActions.setSetupNewNameServerGroupVisible(true));
|
||||
dispatch(
|
||||
nsGroupActions.setNameServerGroup({
|
||||
id: nsGroupToAction?.id,
|
||||
name: nsGroupToAction?.name,
|
||||
primary: nsGroupToAction?.primary,
|
||||
domains: nsGroupToAction?.domains,
|
||||
description: nsGroupToAction?.description,
|
||||
groups: nsGroupToAction?.groups,
|
||||
enabled: nsGroupToAction?.enabled,
|
||||
nameservers: nsGroupToAction?.nameservers,
|
||||
} as NameServerGroup)
|
||||
);
|
||||
};
|
||||
|
||||
const showConfirmDelete = () => {
|
||||
let name = nsGroupToAction ? nsGroupToAction.name : "";
|
||||
const showConfirmDelete = (record: NameserverGroupDataTable) => {
|
||||
setNsGroupToAction(record as NameserverGroupDataTable);
|
||||
let name = record ? record.name : "";
|
||||
confirm({
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
title: 'Delete Nameserver group "' + name + '"',
|
||||
@@ -201,7 +189,7 @@ export const Nameservers = () => {
|
||||
dispatch(
|
||||
nsGroupActions.deleteNameServerGroup.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: nsGroupToAction?.id || "",
|
||||
payload: record?.id || "",
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -227,10 +215,13 @@ export const Nameservers = () => {
|
||||
.filter((g) => groupsMap.get(g))
|
||||
.map((g) => groupsMap.get(g)!);
|
||||
}
|
||||
|
||||
let btn = (
|
||||
<Button type="link" onClick={() => setUserAndView(userToAction)}>
|
||||
{displayGroups.length}
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => setUserAndView(userToAction)}
|
||||
style={{ padding: "0" }}
|
||||
>
|
||||
+{displayGroups && displayGroups.length - 1}
|
||||
</Button>
|
||||
);
|
||||
if (!displayGroups || displayGroups!.length < 1) {
|
||||
@@ -251,13 +242,18 @@ export const Nameservers = () => {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const mainContent = <Space direction="vertical">{content}</Space>;
|
||||
const updateContent =
|
||||
displayGroups && displayGroups.length > 1
|
||||
? content && content?.slice(1)
|
||||
: content;
|
||||
const mainContent = <Space direction="vertical">{updateContent}</Space>;
|
||||
let popoverPlacement = "top";
|
||||
if (content && content.length > 5) {
|
||||
popoverPlacement = "rightTop";
|
||||
}
|
||||
|
||||
return (
|
||||
return displayGroups && displayGroups.length === 1 ? (
|
||||
<> {displayGroups && displayGroups.length && displayGroups[0].name}</>
|
||||
) : (
|
||||
<Popover
|
||||
placement={popoverPlacement as TooltipPlacement}
|
||||
key={userToAction.id}
|
||||
@@ -268,7 +264,9 @@ export const Nameservers = () => {
|
||||
content={mainContent}
|
||||
title={null}
|
||||
>
|
||||
{btn}
|
||||
<span className="d-flex">
|
||||
{displayGroups && displayGroups.length && displayGroups[0].name} {btn}
|
||||
</span>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -283,10 +281,12 @@ export const Nameservers = () => {
|
||||
domains = inputDomains;
|
||||
}
|
||||
|
||||
let btn = (
|
||||
let btn = domains.length ? (
|
||||
<Button type="link" onClick={() => setUserAndView(userToAction)}>
|
||||
{domains.length ? domains.length : 0}
|
||||
{domains.length}
|
||||
</Button>
|
||||
) : (
|
||||
<Tag>ALL</Tag>
|
||||
);
|
||||
if (!domains || domains!.length < 1) {
|
||||
return btn;
|
||||
@@ -325,10 +325,10 @@ export const Nameservers = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (updateNameServerGroupVisible) {
|
||||
if (addNewNameServerGroupVisible) {
|
||||
setGroupPopupVisible("");
|
||||
}
|
||||
}, [updateNameServerGroupVisible]);
|
||||
}, [addNewNameServerGroupVisible]);
|
||||
|
||||
const createKey = "saving";
|
||||
useEffect(() => {
|
||||
@@ -380,8 +380,54 @@ export const Nameservers = () => {
|
||||
}
|
||||
}, [savedNSGroup]);
|
||||
|
||||
const createDeleteKey = "Delete";
|
||||
useEffect(() => {
|
||||
if (deleteNSGroup.loading) {
|
||||
message.loading({
|
||||
content: "Deleting...",
|
||||
key: createDeleteKey,
|
||||
duration: 0,
|
||||
style: styleNotification,
|
||||
});
|
||||
} else if (deleteNSGroup.success) {
|
||||
message.success({
|
||||
content: "Nameserver has been deleted successfully.",
|
||||
key: createDeleteKey,
|
||||
duration: 2,
|
||||
style: styleNotification,
|
||||
});
|
||||
dispatch(nsGroupActions.resetDeletedNameServerGroup(null));
|
||||
} else if (deleteNSGroup.error) {
|
||||
let errorMsg = "Failed to delete nameserver group";
|
||||
switch (deleteNSGroup.error.statusCode) {
|
||||
case 403:
|
||||
errorMsg =
|
||||
"Failed to delete nameserver group. You might not have enough permissions.";
|
||||
break;
|
||||
default:
|
||||
errorMsg = deleteNSGroup.error.data.message
|
||||
? deleteNSGroup.error.data.message
|
||||
: errorMsg;
|
||||
break;
|
||||
}
|
||||
message.error({
|
||||
content: errorMsg,
|
||||
key: createDeleteKey,
|
||||
duration: 5,
|
||||
style: styleNotification,
|
||||
});
|
||||
dispatch(
|
||||
nsGroupActions.setSavedNameServerGroup({
|
||||
...deleteNSGroup,
|
||||
error: null,
|
||||
})
|
||||
);
|
||||
dispatch(nsGroupActions.resetDeletedNameServerGroup(null));
|
||||
}
|
||||
}, [deleteNSGroup]);
|
||||
|
||||
const onPopoverVisibleChange = (b: boolean, key: string) => {
|
||||
if (updateNameServerGroupVisible) {
|
||||
if (addNewNameServerGroupVisible) {
|
||||
setGroupPopupVisible("");
|
||||
} else {
|
||||
if (b) {
|
||||
@@ -392,27 +438,6 @@ export const Nameservers = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const itemsMenuAction = [
|
||||
{
|
||||
key: "edit",
|
||||
label: (
|
||||
<Button type="text" onClick={() => onClickEdit()}>
|
||||
View
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
label: (
|
||||
<Button type="text" onClick={() => showConfirmDelete()}>
|
||||
Delete
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const actionsMenu = <Menu items={itemsMenuAction}></Menu>;
|
||||
|
||||
const onClickAddNewNSGroup = () => {
|
||||
dispatch(nsGroupActions.setSetupNewNameServerGroupVisible(true));
|
||||
dispatch(
|
||||
@@ -423,32 +448,41 @@ export const Nameservers = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeDisabled = (checked: boolean, record: any) => {
|
||||
dispatch(
|
||||
nsGroupActions.saveNameServerGroup.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: { ...record, enabled: checked },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{nsGroup.length ? (
|
||||
<Paragraph style={{marginTop: "5px"}}>
|
||||
Add nameservers for domain name resolution in your NetBird network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
<Paragraph style={{ marginTop: "5px" }}>
|
||||
Add nameservers for domain name resolution in your NetBird network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Paragraph style={{marginTop: "5px"}} type={"secondary"}>
|
||||
Add nameservers for domain name resolution in your NetBird network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
<Paragraph style={{ marginTop: "5px" }} type={"secondary"}>
|
||||
Add nameservers for domain name resolution in your NetBird network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
)}
|
||||
<Space direction="vertical" size="large" style={{ display: "flex" }}>
|
||||
<Row gutter={[16, 24]}>
|
||||
@@ -484,7 +518,11 @@ export const Nameservers = () => {
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
{!showTutorial && (
|
||||
<Button type="primary" onClick={onClickAddNewNSGroup}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onClickAddNewNSGroup}
|
||||
disabled={savedNSGroup.loading}
|
||||
>
|
||||
Add Nameserver
|
||||
</Button>
|
||||
)}
|
||||
@@ -502,161 +540,157 @@ export const Nameservers = () => {
|
||||
/>
|
||||
)}
|
||||
<Card bodyStyle={{ padding: 0 }}>
|
||||
{!showTutorial && ( <Table
|
||||
pagination={{
|
||||
pageSize,
|
||||
showSizeChanger: false,
|
||||
showTotal: (total, range) =>
|
||||
`Showing ${range[0]} to ${range[1]} of ${total} nameservers`,
|
||||
}}
|
||||
// className="card-table"
|
||||
className={`access-control-table ${
|
||||
showTutorial
|
||||
? "card-table card-table-no-placeholder"
|
||||
: "card-table"
|
||||
}`}
|
||||
showSorterTooltip={false}
|
||||
scroll={{ x: true }}
|
||||
loading={tableSpin(loading)}
|
||||
dataSource={dataTable}
|
||||
>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
align="center"
|
||||
onFilter={(value: string | number | boolean, record) =>
|
||||
(record as any).name.includes(value)
|
||||
}
|
||||
sorter={(a, b) => (a as any).name.localeCompare((b as any).name)}
|
||||
defaultSortOrder="ascend"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() =>
|
||||
setUserAndView(record as NameserverGroupDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
{text && text.trim() !== ""
|
||||
? text
|
||||
: (record as NameServerGroup).id}
|
||||
</Button>
|
||||
);
|
||||
{!showTutorial && (
|
||||
<Table
|
||||
pagination={{
|
||||
pageSize,
|
||||
showSizeChanger: false,
|
||||
showTotal: (total, range) =>
|
||||
`Showing ${range[0]} to ${range[1]} of ${total} nameservers`,
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Status"
|
||||
dataIndex="enabled"
|
||||
align="center"
|
||||
render={(text: Boolean) => {
|
||||
return text ? (
|
||||
<Tag color="green">enabled</Tag>
|
||||
) : (
|
||||
<Tag color="red">disabled</Tag>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Nameservers"
|
||||
dataIndex="nameservers"
|
||||
align="center"
|
||||
render={(nameservers: NameServer[]) => (
|
||||
<>
|
||||
{nameservers.map((nameserver) => (
|
||||
<Tag key={nameserver.ip}>{nameserver.ip}</Tag>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Column
|
||||
title="All domains"
|
||||
dataIndex="primary"
|
||||
align="center"
|
||||
render={(text: Boolean) => {
|
||||
return text ? <Tag color="blue">yes</Tag> : <Tag>no</Tag>;
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Match domains"
|
||||
dataIndex="domains"
|
||||
align="center"
|
||||
render={(text, record: NameserverGroupDataTable) => {
|
||||
return renderPopoverDomains(text, record.domains, record);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Groups"
|
||||
dataIndex="groupsCount"
|
||||
align="center"
|
||||
render={(text, record: NameserverGroupDataTable) => {
|
||||
return renderPopoverGroups(text, record.groups, record);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title=""
|
||||
align="center"
|
||||
width="30px"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={["click"]}
|
||||
overlay={actionsMenu}
|
||||
onOpenChange={(visible) => {
|
||||
if (visible)
|
||||
setNsGroupToAction(record as NameserverGroupDataTable);
|
||||
}}
|
||||
>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<EllipsisOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>)}
|
||||
{showTutorial && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="center"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "45px 15px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Title level={4} style={{ textAlign: "center" }}>
|
||||
Create Nameserver
|
||||
</Title>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-line",
|
||||
}}
|
||||
>
|
||||
It looks like you don't have any nameservers. {"\n"}
|
||||
Get started by adding one to your network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
className={`access-control-table ${
|
||||
showTutorial
|
||||
? "card-table card-table-no-placeholder"
|
||||
: "card-table"
|
||||
}`}
|
||||
showSorterTooltip={false}
|
||||
scroll={{ x: true }}
|
||||
loading={tableSpin(loading)}
|
||||
dataSource={dataTable}
|
||||
>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
align="center"
|
||||
onFilter={(value: string | number | boolean, record) =>
|
||||
(record as any).name.includes(value)
|
||||
}
|
||||
sorter={(a, b) =>
|
||||
(a as any).name.localeCompare((b as any).name)
|
||||
}
|
||||
defaultSortOrder="ascend"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Button
|
||||
size={"middle"}
|
||||
type="primary"
|
||||
onClick={() => onClickAddNewNSGroup()}
|
||||
type="text"
|
||||
onClick={() =>
|
||||
setUserAndView(record as NameserverGroupDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
Add nameserver
|
||||
{text && text.trim() !== ""
|
||||
? text
|
||||
: (record as NameServerGroup).id}
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Enabled"
|
||||
dataIndex="enabled"
|
||||
align="center"
|
||||
render={(text: Boolean, record) => {
|
||||
return (
|
||||
<Switch
|
||||
onChange={(isChecked) =>
|
||||
handleChangeDisabled(isChecked, record)
|
||||
}
|
||||
disabled={savedNSGroup.loading}
|
||||
defaultChecked={!!text}
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Nameservers"
|
||||
dataIndex="nameservers"
|
||||
align="center"
|
||||
render={(nameservers: NameServer[]) => (
|
||||
<>
|
||||
{nameservers.map((nameserver) => (
|
||||
<Tag key={nameserver.ip}>{nameserver.ip}</Tag>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Column
|
||||
title="Match domains"
|
||||
dataIndex="domains"
|
||||
align="center"
|
||||
render={(text, record: NameserverGroupDataTable) => {
|
||||
return renderPopoverDomains(text, record.domains, record);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Distribution groups"
|
||||
dataIndex="groupsCount"
|
||||
align="center"
|
||||
render={(text, record: NameserverGroupDataTable) => {
|
||||
return renderPopoverGroups(text, record.groups, record);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title=""
|
||||
align="center"
|
||||
width="30px"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
disabled={savedNSGroup.loading}
|
||||
onClick={() =>
|
||||
showConfirmDelete(record as NameserverGroupDataTable)
|
||||
}
|
||||
danger={true}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
)}
|
||||
{showTutorial && (
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
align="center"
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: "45px 15px",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Title level={4} style={{ textAlign: "center" }}>
|
||||
Create Nameserver
|
||||
</Title>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-line",
|
||||
}}
|
||||
>
|
||||
It looks like you don't have any nameservers. {"\n"}
|
||||
Get started by adding one to your network.
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
|
||||
>
|
||||
{" "}
|
||||
Learn more
|
||||
</a>
|
||||
</Paragraph>
|
||||
<Button
|
||||
size={"middle"}
|
||||
type="primary"
|
||||
onClick={() => onClickAddNewNSGroup()}
|
||||
>
|
||||
Add nameserver
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</Card>
|
||||
</Space>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user