Skip to content

Commit 61e11d3

Browse files
authored
Apply recent cloud changes (#447)
* Add resource description, add single resource for acl, add icons for group badges, add inactivity expiration * Add extra dns labels, remove routing restriction
1 parent c8e3b50 commit 61e11d3

File tree

78 files changed

+1994
-653
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1994
-653
lines changed

src/app/(dashboard)/peer/page.tsx

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ import Separator from "@components/Separator";
2323
import FullScreenLoading from "@components/ui/FullScreenLoading";
2424
import LoginExpiredBadge from "@components/ui/LoginExpiredBadge";
2525
import TextWithTooltip from "@components/ui/TextWithTooltip";
26-
import { getOperatingSystem } from "@hooks/useOperatingSystem";
2726
import useRedirect from "@hooks/useRedirect";
28-
import { IconCloudLock, IconInfoCircle } from "@tabler/icons-react";
2927
import useFetchApi from "@utils/api";
28+
import { cn } from "@utils/helpers";
3029
import dayjs from "dayjs";
3130
import { isEmpty, trim } from "lodash";
3231
import {
@@ -41,6 +40,7 @@ import {
4140
NetworkIcon,
4241
PencilIcon,
4342
TerminalSquare,
43+
TimerResetIcon,
4444
} from "lucide-react";
4545
import { useRouter, useSearchParams } from "next/navigation";
4646
import { toASCII } from "punycode";
@@ -56,11 +56,11 @@ import PeerProvider, { usePeer } from "@/contexts/PeerProvider";
5656
import RoutesProvider from "@/contexts/RoutesProvider";
5757
import { useLoggedInUser } from "@/contexts/UsersProvider";
5858
import { useHasChanges } from "@/hooks/useHasChanges";
59-
import { OperatingSystem } from "@/interfaces/OperatingSystem";
6059
import type { Peer } from "@/interfaces/Peer";
6160
import PageContainer from "@/layouts/PageContainer";
6261
import useGroupHelper from "@/modules/groups/useGroupHelper";
6362
import { AccessiblePeersSection } from "@/modules/peer/AccessiblePeersSection";
63+
import { PeerExpirationToggle } from "@/modules/peer/PeerExpirationToggle";
6464
import { PeerNetworkRoutesSection } from "@/modules/peer/PeerNetworkRoutesSection";
6565

6666
export default function PeerPage() {
@@ -70,9 +70,16 @@ export default function PeerPage() {
7070

7171
useRedirect("/peers", false, !peerId);
7272

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+
7380
return peer && !isLoading ? (
7481
<PeerProvider peer={peer} key={peerId}>
75-
<PeerOverview />
82+
<PeerOverview key={peerKey} />
7683
</PeerProvider>
7784
) : (
7885
<FullScreenLoading />
@@ -89,20 +96,15 @@ function PeerOverview() {
8996
const [loginExpiration, setLoginExpiration] = useState(
9097
peer.login_expiration_enabled,
9198
);
99+
const [inactivityExpiration, setInactivityExpiration] = useState(
100+
peer.inactivity_expiration_enabled,
101+
);
92102
const [selectedGroups, setSelectedGroups, { getAllGroupCalls }] =
93103
useGroupHelper({
94104
initial: peerGroups,
95105
peer,
96106
});
97107

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-
106108
/**
107109
* Detect if there are changes in the peer information, if there are changes, then enable the save button.
108110
*/
@@ -111,10 +113,16 @@ function PeerOverview() {
111113
ssh,
112114
selectedGroups,
113115
loginExpiration,
116+
inactivityExpiration,
114117
]);
115118

116119
const updatePeer = async () => {
117-
const updateRequest = update(name, ssh, loginExpiration);
120+
const updateRequest = update({
121+
name,
122+
ssh,
123+
loginExpiration,
124+
inactivityExpiration,
125+
});
118126
const groupCalls = getAllGroupCalls();
119127
const batchCall = groupCalls
120128
? [...groupCalls, updateRequest]
@@ -125,13 +133,19 @@ function PeerOverview() {
125133
promise: Promise.all(batchCall).then(() => {
126134
mutate("/peers/" + peer.id);
127135
mutate("/groups");
128-
updateHasChangedRef([name, ssh, selectedGroups, loginExpiration]);
136+
updateHasChangedRef([
137+
name,
138+
ssh,
139+
selectedGroups,
140+
loginExpiration,
141+
inactivityExpiration,
142+
]);
129143
}),
130144
loadingMessage: "Saving the peer...",
131145
});
132146
};
133147

134-
const { isUser } = useLoggedInUser();
148+
const { isUser, isOwnerOrAdmin } = useLoggedInUser();
135149

136150
return (
137151
<PageContainer>
@@ -213,53 +227,43 @@ function PeerOverview() {
213227
<div className={"flex gap-10 w-full mt-5 max-w-6xl"}>
214228
<PeerInformationCard peer={peer} />
215229

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 && (
219242
<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",
242248
)}
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+
/>
243263
</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+
263267
<FullTooltip
264268
content={
265269
<div
@@ -317,7 +321,7 @@ function PeerOverview() {
317321
</div>
318322
</div>
319323

320-
{isLinux && !isUser ? (
324+
{!isUser ? (
321325
<>
322326
<Separator />
323327
<PeerNetworkRoutesSection peer={peer} />
@@ -335,7 +339,7 @@ function PeerOverview() {
335339
);
336340
}
337341

338-
function PeerInformationCard({ peer }: { peer: Peer }) {
342+
function PeerInformationCard({ peer }: Readonly<{ peer: Peer }>) {
339343
const { isLoading, getRegionByPeer } = useCountries();
340344

341345
const countryText = useMemo(() => {
@@ -371,14 +375,20 @@ function PeerInformationCard({ peer }: { peer: Peer }) {
371375

372376
<Card.ListItem
373377
copy
374-
copyText={"Domain name"}
378+
copyText={"DNS label"}
375379
label={
376380
<>
377381
<Globe size={16} />
378382
Domain Name
379383
</>
380384
}
385+
className={
386+
peer?.extra_dns_labels && peer.extra_dns_labels.length > 0
387+
? "items-start"
388+
: ""
389+
}
381390
value={peer.dns_label}
391+
extraText={peer?.extra_dns_labels}
382392
/>
383393

384394
<Card.ListItem
@@ -489,7 +499,7 @@ interface ModalProps {
489499
peer: Peer;
490500
initialName: string;
491501
}
492-
function EditNameModal({ onSuccess, peer, initialName }: ModalProps) {
502+
function EditNameModal({ onSuccess, peer, initialName }: Readonly<ModalProps>) {
493503
const [name, setName] = useState(initialName);
494504

495505
const isDisabled = useMemo(() => {

src/app/(dashboard)/team/user/page.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function UserPage() {
4040
const { data: users, isLoading } = useFetchApi<User[]>(
4141
`/users?service_user=${isServiceUser}`,
4242
);
43+
const { isOwnerOrAdmin } = useLoggedInUser();
4344

4445
const user = useMemo(() => {
4546
return users?.find((u) => u.id === userId);
@@ -49,11 +50,15 @@ export default function UserPage() {
4950

5051
const userGroups = useGroupIdsToGroups(user?.auto_groups);
5152

52-
return !isLoading && user && userGroups !== undefined ? (
53-
<UserOverview user={user} initialGroups={userGroups} />
54-
) : (
55-
<FullScreenLoading />
56-
);
53+
if (!isOwnerOrAdmin && user && !isLoading) {
54+
return <UserOverview user={user} initialGroups={[]} />;
55+
}
56+
57+
if (isOwnerOrAdmin && user && !isLoading && userGroups) {
58+
return <UserOverview user={user} initialGroups={userGroups} />;
59+
}
60+
61+
return <FullScreenLoading />;
5762
}
5863

5964
type Props = {
@@ -195,7 +200,7 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
195200
<div className={"flex gap-10 w-full mt-8 max-w-6xl items-start"}>
196201
<UserInformationCard user={user} />
197202
<div className={"flex flex-col gap-8 w-1/2 "}>
198-
{!user.is_service_user && (
203+
{!user.is_service_user && isOwnerOrAdmin && (
199204
<div>
200205
<Label>Auto-assigned groups</Label>
201206
<HelpText>

src/assets/icons/CircleIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function CircleIcon({
1212
size = 11,
1313
inactiveDot = "gray",
1414
className,
15-
}: Props) {
15+
}: Readonly<Props>) {
1616
return (
1717
<span
1818
style={{ width: size + "px", height: size + "px" }}

src/assets/icons/EntraIcon.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function EntraIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
width="231"
7+
height="231"
8+
viewBox="0 0 231 231"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
{...iconProperties(props)}
12+
>
13+
<path
14+
d="M48.7923 180.077C53.7717 183.183 62.0492 186.635 70.8015 186.635C78.771 186.635 86.1758 184.325 92.3102 180.385C92.3102 180.385 92.323 180.385 92.3358 180.373L115.5 165.896V218.167C111.83 218.167 108.134 217.166 104.925 215.164L48.7923 180.077Z"
15+
fill="#225086"
16+
/>
17+
<path
18+
d="M100.78 19.3398L4.53017 127.91C-2.90033 136.303 -0.962501 148.982 8.67533 155.001C8.67533 155.001 44.3007 177.267 48.7923 180.077C53.7717 183.183 62.0492 186.635 70.8015 186.635C78.771 186.635 86.1758 184.325 92.3102 180.385C92.3102 180.385 92.323 180.385 92.3358 180.373L115.5 165.896L59.4953 130.887L115.513 67.6958V12.8333C110.072 12.8333 104.63 15.0022 100.78 19.3398Z"
19+
fill="#66DDFF"
20+
/>
21+
<path
22+
d="M59.4953 130.887L60.1627 131.298L115.5 165.896H115.513V67.7087L115.5 67.6958L59.4953 130.887Z"
23+
fill="#CBF8FF"
24+
/>
25+
<path
26+
d="M222.325 155.001C231.963 148.982 233.9 136.303 226.47 127.91L163.317 56.672C158.222 54.2978 152.511 52.9375 146.467 52.9375C134.596 52.9375 123.983 58.058 116.925 66.1045L115.526 67.683L171.53 130.874L115.513 165.884V218.154C119.196 218.154 122.866 217.153 126.075 215.151L222.325 154.988V155.001Z"
27+
fill="#074793"
28+
/>
29+
<path
30+
d="M115.513 12.8333V67.6958L116.912 66.1173C123.97 58.0708 134.583 52.9503 146.454 52.9503C152.511 52.9503 158.209 54.3235 163.304 56.6848L130.207 19.3527C126.37 15.015 120.929 12.8462 115.5 12.8462L115.513 12.8333Z"
31+
fill="#0294E4"
32+
/>
33+
<path
34+
d="M171.518 130.887L115.513 67.7087V165.884L171.518 130.887Z"
35+
fill="#96BCC2"
36+
/>
37+
</svg>
38+
);
39+
}

src/assets/icons/GoogleIcon.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function GoogleIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
xmlns="http://www.w3.org/2000/svg"
7+
height="24"
8+
viewBox="0 0 24 24"
9+
width="24"
10+
{...iconProperties(props)}
11+
>
12+
<path
13+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
14+
fill="#4285F4"
15+
/>
16+
<path
17+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
18+
fill="#34A853"
19+
/>
20+
<path
21+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
22+
fill="#FBBC05"
23+
/>
24+
<path
25+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
26+
fill="#EA4335"
27+
/>
28+
<path d="M1 1h22v22H1z" fill="none" />
29+
</svg>
30+
);
31+
}

0 commit comments

Comments
 (0)