From 09ae157be3ea191c4ee5db3163ed69acecbcce74 Mon Sep 17 00:00:00 2001 From: Sarooj bukhari Date: Mon, 10 Jul 2023 12:03:27 +0500 Subject: [PATCH] Re-work DNS layout (#222) --- src/components/NameServerGroupAdd.tsx | 908 ++++++++++++++ src/components/NameServerGroupUpdate.tsx | 1383 ++++++++++++---------- src/components/PeerUpdate.tsx | 1 - src/components/RoutePeerUpdate.tsx | 1 - src/components/RouteUpdate.tsx | 1 - src/index.css | 26 + src/store/nameservers/actions.ts | 1 + src/store/nameservers/reducer.ts | 14 +- src/store/nameservers/sagas.ts | 4 +- src/store/nameservers/types.ts | 29 +- src/store/policy/sagas.ts | 16 +- src/views/AccessControl.tsx | 1 - src/views/DNS.tsx | 145 +-- src/views/Nameservers.tsx | 502 ++++---- 14 files changed, 2073 insertions(+), 959 deletions(-) create mode 100644 src/components/NameServerGroupAdd.tsx diff --git a/src/components/NameServerGroupAdd.tsx b/src/components/NameServerGroupAdd.tsx new file mode 100644 index 0000000..d7edf45 --- /dev/null +++ b/src/components/NameServerGroupAdd.tsx @@ -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(null); + const inputDescriptionRef = useRef(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 + ) => ( + +
+ + + {!!fields.length && ( + + + + + + + Nameserver IP + + + + + Port + + + + + )} + {fields.map((field, index) => { + return ( + + + + + + + + + + + + + + + + + + remove(field.name)} /> + + + ); + })} + + + + + + + + + +
+ + ); + + // @ts-ignore + const renderDomains = ( + fields: FormListFieldData[], + { add, remove }: any, + { errors }: any + ) => ( + <> + + + + + + Add domain if you want to have a specific one + + + + + {fields.map((field, index) => { + return ( + + + + + + + + remove(field.name)} + /> + + + ); + })} + + + + + + + + + + + ); + + const handleChangeDisabled = (checked: boolean) => { + setFormNSGroup({ + ...formNSGroup, + enabled: checked, + }); + }; + + return ( + <> + {nsGroup && ( + + + + + Add Nameserver + + + Use this nameserver to resolve domains in your network + + + + {selectCustom ? ( +
+ + +
+ + + {!editName && !editDescription && formNSGroup.id && ( + + )} + + + {!editName && formNSGroup.id ? ( +
toggleEditName(true)} + > + {formNSGroup.id + ? formNSGroup.name + : "New nameserver group"} +
+ ) : ( +
+ + + toggleEditName(false)} + onBlur={() => toggleEditName(false)} + autoComplete="off" + maxLength={40} + /> + +
+ )} + {!editDescription ? ( +
toggleEditDescription(true)} + > + {formNSGroup.description && + formNSGroup.description.trim() !== "" + ? formNSGroup.description + : "Add description"} +
+ ) : ( +
+ + + + toggleEditDescription(false) + } + onBlur={() => toggleEditDescription(false)} + autoComplete="off" + /> + +
+ )} + +
+
+ + + + {renderNSList} + + + + {renderDomains} + + + + + Advertise this route to peers that belong to the following + groups + + + + + + + +
+ +
+ + + Disable this server if you don't want it to apply + immediately + +
+
+
+ + + + Learn more about + + {" "} + DNS + + + + + + + + + +
+
+ ) : ( + <> + + + + + Select a predefined one + + + + + + - - - - - - - - - - - - - - - - remove(field.name)}/> - - - ) - })} - - - - - - - ) - - // @ts-ignore - const renderDomains = (fields: FormListFieldData[], {add, remove}, {errors}) => ( - <> - - - - Match domains - - - - - - - - - {fields.map((field, index) => { - return ( - - - - - - - ) - })} - - - - - - - ) - - return ( - <> - {nsGroup && - - - - - } + {!!fields.length && ( + + + + + + + Nameserver IP + + + + Port + + + + )} + {fields.map((field, index) => { + return ( + + + - {selectCustom ? - (
+ + + + + + + + + + + + + + + + remove(field.name)} /> + + + ); + })} + + + + + + + + + + + + ); + + // @ts-ignore + const renderDomains = ( + fields: FormListFieldData[], + { add, remove }: any, + { errors }: any + ) => ( +
+ + + + + + Add domain if you want to have a specific one + + + {/* + + + + */} + + + {fields.map((field, index) => { + return ( + + + + + + + ); + })} + + + + + + + + + +
+ ); + + const handleChangeDisabled = (checked: boolean) => { + setFormNSGroup({ + ...formNSGroup, + enabled: checked, + }); + }; + + const onBreadcrumbUsersClick = () => { + onCancel(); + }; + + return ( + <> + + DNS, + }, + { + title: formNSGroup.name, + }, + ]} + /> + + + + +
+ + + {!editName && formNSGroup.id ? ( +
toggleEditName(true)} + style={{ + fontSize: "22px", + margin: " 0px 0px 10px", + cursor: "pointer", + fontWeight: "500", + lineHeight: "24px", + }} > - - -
- - - {!editName && !editDescription && formNSGroup.id && - - } - - - {!editName && formNSGroup.id ? ( -
toggleEditName(true)}>{formNSGroup.id ? formNSGroup.name : 'New nameserver group'}
- ) : ( - - toggleEditName(false)} - onBlur={() => toggleEditName(false)} autoComplete="off" - maxLength={40}/> - - )} - {!editDescription ? ( -
toggleEditDescription(true)}> - {formNSGroup.description && formNSGroup.description.trim() !== "" ? formNSGroup.description : 'Add description...'} -
- ) : ( - - toggleEditDescription(false)} - onBlur={() => toggleEditDescription(false)} - autoComplete="off"/> - - )} - -
- - + {formNSGroup.id + ? formNSGroup.name + : "New nameserver group"} +
+ ) : ( + + +
+ + + toggleEditName(false)} + onBlur={() => toggleEditName(false)} + autoComplete="off" + maxLength={40} + /> + +
+ +
+ )} + {!editDescription ? ( +
toggleEditDescription(true)} + > + {formNSGroup.description && + formNSGroup.description.trim() !== "" + ? formNSGroup.description + : "Add description"} +
+ ) : ( + + +
+ + + + toggleEditDescription(false) + } + onBlur={() => toggleEditDescription(false)} + autoComplete="off" + /> + +
+ +
+ )} + +
+
+ + + +
+ +
+ + + Disable this server if you don't want it to apply + immediately + +
+
+
+ + + + {renderNSList} + + - -
+ + {renderDomains} + + + + + + + + + + + + + + + +
+
+ + ); +}; - - - - - - - - - - - - - - {renderNSList} - - - - - - - - - - {renderDomains} - - - - - - - - - - - - - - - - - Nameservers let you define resolvers for your DNS queries. - Because not all operating systems support match-only domain resolution, - you should define at least one set of nameservers to resolve all domains - per distribution group. - - - - - - - - -
- ) : - ( - - - - Select a predefined nameserver - - - - -