1
1
import Breadcrumbs from "@components/Breadcrumbs" ;
2
+ import Button from "@components/Button" ;
2
3
import FancyToggleSwitch from "@components/FancyToggleSwitch" ;
4
+ import HelpText from "@components/HelpText" ;
5
+ import InlineLink from "@components/InlineLink" ;
6
+ import { Input } from "@components/Input" ;
7
+ import { Label } from "@components/Label" ;
3
8
import { notify } from "@components/Notification" ;
9
+ import { useHasChanges } from "@hooks/useHasChanges" ;
4
10
import * as Tabs from "@radix-ui/react-tabs" ;
5
11
import { useApiCall } from "@utils/api" ;
6
- import { GlobeIcon , NetworkIcon } from "lucide-react" ;
7
- import React , { useState } from "react" ;
12
+ import { validator } from "@utils/helpers" ;
13
+ import { isNetBirdHosted } from "@utils/netbird" ;
14
+ import { ExternalLinkIcon , GlobeIcon , NetworkIcon } from "lucide-react" ;
15
+ import React , { useMemo , useState } from "react" ;
8
16
import { useSWRConfig } from "swr" ;
9
17
import SettingsIcon from "@/assets/icons/SettingsIcon" ;
10
18
import { Account } from "@/interfaces/Account" ;
@@ -13,18 +21,23 @@ type Props = {
13
21
account : Account ;
14
22
} ;
15
23
16
- export default function NetworkSettingsTab ( { account } : Props ) {
24
+ export default function NetworkSettingsTab ( { account } : Readonly < Props > ) {
17
25
const { mutate } = useSWRConfig ( ) ;
18
- const saveRequest = useApiCall < Account > ( "/accounts/" + account . id ) ;
26
+ const saveRequest = useApiCall < Account > ( "/accounts/" + account . id , true ) ;
19
27
20
28
const [ routingPeerDNSSetting , setRoutingPeerDNSSetting ] = useState (
21
29
account . settings . routing_peer_dns_resolution_enabled ,
22
30
) ;
31
+ const [ customDNSDomain , setCustomDNSDomain ] = useState (
32
+ account . settings . dns_domain || "" ,
33
+ ) ;
23
34
24
- const toggleSetting = async ( toggle : boolean ) => {
35
+ const toggleNetworkDNSSetting = async ( toggle : boolean ) => {
25
36
notify ( {
26
- title : "Save Network Settings" ,
27
- description : "Network settings successfully saved." ,
37
+ title : "DNS Wildcard Routing" ,
38
+ description : `DNS Wildcard Routing successfully ${
39
+ toggle ? "enabled" : "disabled"
40
+ } .`,
28
41
promise : saveRequest
29
42
. put ( {
30
43
id : account . id ,
@@ -37,10 +50,43 @@ export default function NetworkSettingsTab({ account }: Props) {
37
50
setRoutingPeerDNSSetting ( toggle ) ;
38
51
mutate ( "/accounts" ) ;
39
52
} ) ,
40
- loadingMessage : "Saving the network settings ..." ,
53
+ loadingMessage : "Updating DNS wildcard setting ..." ,
41
54
} ) ;
42
55
} ;
43
56
57
+ const { hasChanges, updateRef } = useHasChanges ( [ customDNSDomain ] ) ;
58
+
59
+ const saveChanges = async ( ) => {
60
+ notify ( {
61
+ title : "Custom DNS Domain" ,
62
+ description : `Custom DNS Domain successfully updated.` ,
63
+ promise : saveRequest
64
+ . put ( {
65
+ id : account . id ,
66
+ settings : {
67
+ ...account . settings ,
68
+ dns_domain : customDNSDomain || "" ,
69
+ } ,
70
+ } )
71
+ . then ( ( ) => {
72
+ mutate ( "/accounts" ) ;
73
+ updateRef ( [ customDNSDomain ] ) ;
74
+ } ) ,
75
+ loadingMessage : "Updating Custom DNS domain..." ,
76
+ } ) ;
77
+ } ;
78
+
79
+ const domainError = useMemo ( ( ) => {
80
+ if ( customDNSDomain == "" ) return "" ;
81
+ const valid = validator . isValidDomain ( customDNSDomain , {
82
+ allowWildcard : false ,
83
+ allowOnlyTld : false ,
84
+ } ) ;
85
+ if ( ! valid ) {
86
+ return "Please enter a valid domain, e.g. example.com or intra.example.com" ;
87
+ }
88
+ } , [ customDNSDomain ] ) ;
89
+
44
90
return (
45
91
< Tabs . Content value = { "networks" } >
46
92
< div className = { "p-default py-6 max-w-2xl" } >
@@ -51,36 +97,80 @@ export default function NetworkSettingsTab({ account }: Props) {
51
97
icon = { < SettingsIcon size = { 13 } /> }
52
98
/>
53
99
< Breadcrumbs . Item
54
- href = { "/settings#network " }
55
- label = { "Network " }
100
+ href = { "/settings?tab=networks " }
101
+ label = { "Networks " }
56
102
icon = { < NetworkIcon size = { 14 } /> }
57
103
active
58
104
/>
59
105
</ Breadcrumbs >
60
106
< div className = { "flex items-start justify-between" } >
61
- < h1 > Networks</ h1 >
107
+ < div >
108
+ < h1 > Networks</ h1 >
109
+ </ div >
110
+ < Button
111
+ variant = { "primary" }
112
+ disabled = { ! hasChanges }
113
+ onClick = { saveChanges }
114
+ >
115
+ Save Changes
116
+ </ Button >
62
117
</ div >
63
118
64
119
< div className = { "flex flex-col gap-6 w-full mt-8" } >
65
120
< div >
66
- < FancyToggleSwitch
67
- value = { routingPeerDNSSetting }
68
- onChange = { toggleSetting }
69
- label = {
70
- < >
71
- < GlobeIcon size = { 15 } />
72
- Enable DNS Wildcard Routing
73
- </ >
74
- }
75
- helpText = {
76
- < >
77
- Allow routing using DNS wildcards. This requires NetBird
78
- client v0.35 or higher. Changes will only take effect after
79
- restarting the clients.
80
- </ >
121
+ < div
122
+ className = {
123
+ "flex flex-col gap-1 sm:flex-row w-full sm:gap-4 items-center"
81
124
}
82
- />
125
+ >
126
+ < div className = { "min-w-[330px]" } >
127
+ < Label > DNS Domain</ Label >
128
+ < HelpText >
129
+ Specify a custom DNS domain for your network. This will be
130
+ used for all your peers.
131
+ </ HelpText >
132
+ </ div >
133
+ < div className = { "w-full" } >
134
+ < Input
135
+ placeholder = {
136
+ isNetBirdHosted ( ) ? "netbird.cloud" : "netbird.selfhosted"
137
+ }
138
+ errorTooltip = { true }
139
+ errorTooltipPosition = { "top" }
140
+ error = { domainError }
141
+ value = { customDNSDomain }
142
+ onChange = { ( e ) => setCustomDNSDomain ( e . target . value ) }
143
+ />
144
+ </ div >
145
+ </ div >
83
146
</ div >
147
+
148
+ < FancyToggleSwitch
149
+ value = { routingPeerDNSSetting }
150
+ onChange = { toggleNetworkDNSSetting }
151
+ label = {
152
+ < >
153
+ < GlobeIcon size = { 15 } />
154
+ Enable DNS Wildcard Routing
155
+ </ >
156
+ }
157
+ helpText = {
158
+ < >
159
+ Allow routing using DNS wildcards. This requires NetBird client
160
+ v0.35 or higher. Changes will only take effect after restarting
161
+ the clients.{ " " }
162
+ < InlineLink
163
+ href = {
164
+ "https://docs.netbird.io/how-to/accessing-entire-domains-within-networks#enabling-dns-wildcard-routing"
165
+ }
166
+ target = { "_blank" }
167
+ >
168
+ Learn more
169
+ < ExternalLinkIcon size = { 12 } />
170
+ </ InlineLink >
171
+ </ >
172
+ }
173
+ />
84
174
</ div >
85
175
</ div >
86
176
</ Tabs . Content >
0 commit comments