@@ -23,10 +23,9 @@ import Separator from "@components/Separator";
23
23
import FullScreenLoading from "@components/ui/FullScreenLoading" ;
24
24
import LoginExpiredBadge from "@components/ui/LoginExpiredBadge" ;
25
25
import TextWithTooltip from "@components/ui/TextWithTooltip" ;
26
- import { getOperatingSystem } from "@hooks/useOperatingSystem" ;
27
26
import useRedirect from "@hooks/useRedirect" ;
28
- import { IconCloudLock , IconInfoCircle } from "@tabler/icons-react" ;
29
27
import useFetchApi from "@utils/api" ;
28
+ import { cn } from "@utils/helpers" ;
30
29
import dayjs from "dayjs" ;
31
30
import { isEmpty , trim } from "lodash" ;
32
31
import {
@@ -41,6 +40,7 @@ import {
41
40
NetworkIcon ,
42
41
PencilIcon ,
43
42
TerminalSquare ,
43
+ TimerResetIcon ,
44
44
} from "lucide-react" ;
45
45
import { useRouter , useSearchParams } from "next/navigation" ;
46
46
import { toASCII } from "punycode" ;
@@ -56,11 +56,11 @@ import PeerProvider, { usePeer } from "@/contexts/PeerProvider";
56
56
import RoutesProvider from "@/contexts/RoutesProvider" ;
57
57
import { useLoggedInUser } from "@/contexts/UsersProvider" ;
58
58
import { useHasChanges } from "@/hooks/useHasChanges" ;
59
- import { OperatingSystem } from "@/interfaces/OperatingSystem" ;
60
59
import type { Peer } from "@/interfaces/Peer" ;
61
60
import PageContainer from "@/layouts/PageContainer" ;
62
61
import useGroupHelper from "@/modules/groups/useGroupHelper" ;
63
62
import { AccessiblePeersSection } from "@/modules/peer/AccessiblePeersSection" ;
63
+ import { PeerExpirationToggle } from "@/modules/peer/PeerExpirationToggle" ;
64
64
import { PeerNetworkRoutesSection } from "@/modules/peer/PeerNetworkRoutesSection" ;
65
65
66
66
export default function PeerPage ( ) {
@@ -70,9 +70,16 @@ export default function PeerPage() {
70
70
71
71
useRedirect ( "/peers" , false , ! peerId ) ;
72
72
73
+ const peerKey = useMemo ( ( ) => {
74
+ let id = peer ?. id ?? "" ;
75
+ let ssh = peer ?. ssh_enabled ? "1" : "0" ;
76
+ let expiration = peer ?. login_expiration_enabled ? "1" : "0" ;
77
+ return `${ id } -${ ssh } -${ expiration } ` ;
78
+ } , [ peer ] ) ;
79
+
73
80
return peer && ! isLoading ? (
74
81
< PeerProvider peer = { peer } key = { peerId } >
75
- < PeerOverview />
82
+ < PeerOverview key = { peerKey } />
76
83
</ PeerProvider >
77
84
) : (
78
85
< FullScreenLoading />
@@ -89,20 +96,15 @@ function PeerOverview() {
89
96
const [ loginExpiration , setLoginExpiration ] = useState (
90
97
peer . login_expiration_enabled ,
91
98
) ;
99
+ const [ inactivityExpiration , setInactivityExpiration ] = useState (
100
+ peer . inactivity_expiration_enabled ,
101
+ ) ;
92
102
const [ selectedGroups , setSelectedGroups , { getAllGroupCalls } ] =
93
103
useGroupHelper ( {
94
104
initial : peerGroups ,
95
105
peer,
96
106
} ) ;
97
107
98
- /**
99
- * Check the operating system of the peer, if it is linux, then show the routes table, otherwise hide it.
100
- */
101
- const isLinux = useMemo ( ( ) => {
102
- const operatingSystem = getOperatingSystem ( peer . os ) ;
103
- return operatingSystem == OperatingSystem . LINUX ;
104
- } , [ peer . os ] ) ;
105
-
106
108
/**
107
109
* Detect if there are changes in the peer information, if there are changes, then enable the save button.
108
110
*/
@@ -111,10 +113,16 @@ function PeerOverview() {
111
113
ssh ,
112
114
selectedGroups ,
113
115
loginExpiration ,
116
+ inactivityExpiration ,
114
117
] ) ;
115
118
116
119
const updatePeer = async ( ) => {
117
- const updateRequest = update ( name , ssh , loginExpiration ) ;
120
+ const updateRequest = update ( {
121
+ name,
122
+ ssh,
123
+ loginExpiration,
124
+ inactivityExpiration,
125
+ } ) ;
118
126
const groupCalls = getAllGroupCalls ( ) ;
119
127
const batchCall = groupCalls
120
128
? [ ...groupCalls , updateRequest ]
@@ -125,13 +133,19 @@ function PeerOverview() {
125
133
promise : Promise . all ( batchCall ) . then ( ( ) => {
126
134
mutate ( "/peers/" + peer . id ) ;
127
135
mutate ( "/groups" ) ;
128
- updateHasChangedRef ( [ name , ssh , selectedGroups , loginExpiration ] ) ;
136
+ updateHasChangedRef ( [
137
+ name ,
138
+ ssh ,
139
+ selectedGroups ,
140
+ loginExpiration ,
141
+ inactivityExpiration ,
142
+ ] ) ;
129
143
} ) ,
130
144
loadingMessage : "Saving the peer..." ,
131
145
} ) ;
132
146
} ;
133
147
134
- const { isUser } = useLoggedInUser ( ) ;
148
+ const { isUser, isOwnerOrAdmin } = useLoggedInUser ( ) ;
135
149
136
150
return (
137
151
< PageContainer >
@@ -213,53 +227,43 @@ function PeerOverview() {
213
227
< div className = { "flex gap-10 w-full mt-5 max-w-6xl" } >
214
228
< PeerInformationCard peer = { peer } />
215
229
216
- < div className = { "flex flex-col gap-6 w-1/2" } >
217
- < FullTooltip
218
- content = {
230
+ < div className = { "flex flex-col gap-6 w-1/2 transition-all" } >
231
+ < div >
232
+ < PeerExpirationToggle
233
+ peer = { peer }
234
+ value = { loginExpiration }
235
+ icon = { < TimerResetIcon size = { 16 } /> }
236
+ onChange = { ( state ) => {
237
+ setLoginExpiration ( state ) ;
238
+ ! state && setInactivityExpiration ( false ) ;
239
+ } }
240
+ />
241
+ { isOwnerOrAdmin && ! ! peer ?. user_id && (
219
242
< div
220
- className = {
221
- "flex gap-2 items-center !text-nb-gray-300 text-xs"
222
- }
223
- >
224
- { ! peer . user_id ? (
225
- < >
226
- < >
227
- < IconInfoCircle size = { 14 } />
228
- < span >
229
- Login expiration is disabled for all peers added
230
- with an setup-key.
231
- </ span >
232
- </ >
233
- </ >
234
- ) : (
235
- < >
236
- < LockIcon size = { 14 } />
237
- < span >
238
- { `You don't have the required permissions to update this
239
- setting.` }
240
- </ span >
241
- </ >
243
+ className = { cn (
244
+ "border border-nb-gray-900 border-t-0 rounded-b-md bg-nb-gray-940 px-[1.28rem] pt-3 pb-5 flex flex-col gap-4 mx-[0.25rem]" ,
245
+ ! loginExpiration
246
+ ? "opacity-50 pointer-events-none"
247
+ : "bg-nb-gray-930/80" ,
242
248
) }
249
+ >
250
+ < PeerExpirationToggle
251
+ peer = { peer }
252
+ variant = { "blank" }
253
+ value = { inactivityExpiration }
254
+ onChange = { setInactivityExpiration }
255
+ title = { "Require login after disconnect" }
256
+ description = {
257
+ "Enable to require authentication after users disconnect from management for 10 minutes."
258
+ }
259
+ className = {
260
+ ! loginExpiration ? "opacity-40 pointer-events-none" : ""
261
+ }
262
+ />
243
263
</ div >
244
- }
245
- className = { "w-full block" }
246
- disabled = { ! ! peer . user_id && ! isUser }
247
- >
248
- < FancyToggleSwitch
249
- disabled = { ! peer . user_id || isUser }
250
- value = { loginExpiration }
251
- onChange = { setLoginExpiration }
252
- label = {
253
- < >
254
- < IconCloudLock size = { 16 } />
255
- Login Expiration
256
- </ >
257
- }
258
- helpText = {
259
- "Enable to require SSO login peers to re-authenticate when their login expires."
260
- }
261
- />
262
- </ FullTooltip >
264
+ ) }
265
+ </ div >
266
+
263
267
< FullTooltip
264
268
content = {
265
269
< div
@@ -317,7 +321,7 @@ function PeerOverview() {
317
321
</ div >
318
322
</ div >
319
323
320
- { isLinux && ! isUser ? (
324
+ { ! isUser ? (
321
325
< >
322
326
< Separator />
323
327
< PeerNetworkRoutesSection peer = { peer } />
@@ -335,7 +339,7 @@ function PeerOverview() {
335
339
) ;
336
340
}
337
341
338
- function PeerInformationCard ( { peer } : { peer : Peer } ) {
342
+ function PeerInformationCard ( { peer } : Readonly < { peer : Peer } > ) {
339
343
const { isLoading, getRegionByPeer } = useCountries ( ) ;
340
344
341
345
const countryText = useMemo ( ( ) => {
@@ -371,14 +375,20 @@ function PeerInformationCard({ peer }: { peer: Peer }) {
371
375
372
376
< Card . ListItem
373
377
copy
374
- copyText = { "Domain name " }
378
+ copyText = { "DNS label " }
375
379
label = {
376
380
< >
377
381
< Globe size = { 16 } />
378
382
Domain Name
379
383
</ >
380
384
}
385
+ className = {
386
+ peer ?. extra_dns_labels && peer . extra_dns_labels . length > 0
387
+ ? "items-start"
388
+ : ""
389
+ }
381
390
value = { peer . dns_label }
391
+ extraText = { peer ?. extra_dns_labels }
382
392
/>
383
393
384
394
< Card . ListItem
@@ -489,7 +499,7 @@ interface ModalProps {
489
499
peer : Peer ;
490
500
initialName : string ;
491
501
}
492
- function EditNameModal ( { onSuccess, peer, initialName } : ModalProps ) {
502
+ function EditNameModal ( { onSuccess, peer, initialName } : Readonly < ModalProps > ) {
493
503
const [ name , setName ] = useState ( initialName ) ;
494
504
495
505
const isDisabled = useMemo ( ( ) => {
0 commit comments