Skip to content

Commit a918465

Browse files
committed
Merge remote-tracking branch 'origin/main' into update/nextjs-version
2 parents 150a9ce + d7c5f7e commit a918465

File tree

9 files changed

+171
-81
lines changed

9 files changed

+171
-81
lines changed

src/interfaces/Account.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ export interface Account {
1414
jwt_allow_groups: string[];
1515
regular_users_view_blocked: boolean;
1616
routing_peer_dns_resolution_enabled: boolean;
17+
dns_domain: string;
1718
};
1819
}

src/modules/dns-nameservers/NameserverModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ function NameserverInput({
590590
</div>
591591

592592
<Input
593-
maxWidthClass={"max-w-[130px]"}
593+
maxWidthClass={"min-w-[150px] max-w-[150px]"}
594594
customPrefix={"Port"}
595595
placeholder={"53"}
596596
value={port}

src/modules/networks/resources/ResourceSingleAddressInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const ResourceSingleAddressInput = ({ value, onChange }: Props) => {
3131

3232
// Case 1: If it has characters (potential domain) but is not a CIDR block
3333
if (hasChars && !isCIDRBlock) {
34-
if (!validator.isValidDomainWithWildcard(value)) {
34+
if (!validator.isValidDomain(value)) {
3535
return "Please enter a valid domain, e.g. intra.example.com or *.example.com";
3636
}
3737
return ""; // Valid domain

src/modules/peers/PeerNameCell.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export default function PeerNameCell({ peer, linkToPeer = true }: Props) {
2020
return users?.find((user) => user.id === peer.user_id);
2121
}, [users, peer.user_id]);
2222

23+
const displayUserEmailOrName = userOfPeer?.email || userOfPeer?.name;
24+
const displayUserId = userOfPeer?.id || peer?.user_id;
25+
2326
return (
2427
<div>
2528
<div
@@ -40,7 +43,7 @@ export default function PeerNameCell({ peer, linkToPeer = true }: Props) {
4043
}
4144
>
4245
<div className={"text-nb-gray-400 font-light truncate"}>
43-
{userOfPeer?.email}
46+
{displayUserEmailOrName || (displayUserId && `user: ${displayUserId}`)}
4447
</div>
4548
</ActiveInactiveRow>
4649
</div>

src/modules/peers/PeerVersionCell.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,30 @@ import {
66
TooltipTrigger,
77
} from "@components/Tooltip";
88
import MemoizedNetBirdIcon from "@components/ui/MemoizedNetBirdIcon";
9+
import { getOperatingSystem } from "@hooks/useOperatingSystem";
910
import { parseVersionString } from "@utils/version";
1011
import { ArrowRightIcon, ArrowUpCircleIcon } from "lucide-react";
1112
import * as React from "react";
1213
import { useMemo } from "react";
1314
import { useApplicationContext } from "@/contexts/ApplicationProvider";
15+
import { OperatingSystem } from "@/interfaces/OperatingSystem";
1416

1517
type Props = {
1618
version: string;
19+
os: string;
1720
};
18-
export default function PeerVersionCell({ version }: Props) {
21+
export default function PeerVersionCell({ version, os }: Props) {
1922
const { latestVersion, latestUrl } = useApplicationContext();
2023

2124
const updateAvailable = useMemo(() => {
25+
const operatingSystem = getOperatingSystem(os);
26+
if (
27+
operatingSystem === OperatingSystem.IOS ||
28+
operatingSystem === OperatingSystem.ANDROID
29+
)
30+
return false;
2231
return parseVersionString(version) < parseVersionString(latestVersion);
23-
}, [version, latestVersion]);
32+
}, [os, version, latestVersion]);
2433

2534
const updateIcon = useMemo(() => {
2635
return <ArrowUpCircleIcon size={15} className={"text-netbird"} />;

src/modules/peers/PeersTable.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
154154
header: ({ column }) => {
155155
return <DataTableHeader column={column}>Version</DataTableHeader>;
156156
},
157-
cell: ({ row }) => <PeerVersionCell version={row.original.version} />,
157+
cell: ({ row }) => (
158+
<PeerVersionCell version={row.original.version} os={row.original.os} />
159+
),
158160
},
159161
{
160162
id: "status",

src/modules/posture-checks/table/PostureCheckBrowseTable.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ import { Checkbox } from "@components/Checkbox";
33
import { DataTable } from "@components/table/DataTable";
44
import DataTableHeader from "@components/table/DataTableHeader";
55
import DataTableRefreshButton from "@components/table/DataTableRefreshButton";
6-
import { useLocalStorage } from "@hooks/useLocalStorage";
76
import {
87
ColumnDef,
98
RowSelectionState,
109
SortingState,
1110
} from "@tanstack/react-table";
1211
import useFetchApi from "@utils/api";
13-
import { usePathname } from "next/navigation";
1412
import React, { useState } from "react";
1513
import { useSWRConfig } from "swr";
1614
import { PostureCheck } from "@/interfaces/PostureCheck";
@@ -25,18 +23,14 @@ export default function PostureCheckBrowseTable({ onAdd }: Readonly<Props>) {
2523
const { data: postureChecks, isLoading } =
2624
useFetchApi<PostureCheck[]>("/posture-checks");
2725
const { mutate } = useSWRConfig();
28-
const path = usePathname();
2926

3027
// Default sorting state of the table
31-
const [sorting, setSorting] = useLocalStorage<SortingState>(
32-
"netbird-table-sort" + path,
33-
[
34-
{
35-
id: "name",
36-
desc: true,
37-
},
38-
],
39-
);
28+
const [sorting, setSorting] = useState<SortingState>([
29+
{
30+
id: "name",
31+
desc: true,
32+
},
33+
]);
4034

4135
const [selectedRows, setSelectedRows] = useState<RowSelectionState>({});
4236

@@ -47,6 +41,7 @@ export default function PostureCheckBrowseTable({ onAdd }: Readonly<Props>) {
4741
rowSelection={selectedRows}
4842
setRowSelection={setSelectedRows}
4943
isLoading={isLoading}
44+
keepStateInLocalStorage={false}
5045
text={"Posture Check"}
5146
sorting={sorting}
5247
wrapperClassName={""}

src/modules/settings/NetworkSettingsTab.tsx

Lines changed: 117 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import Breadcrumbs from "@components/Breadcrumbs";
2+
import Button from "@components/Button";
23
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";
38
import { notify } from "@components/Notification";
9+
import { useHasChanges } from "@hooks/useHasChanges";
410
import * as Tabs from "@radix-ui/react-tabs";
511
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";
816
import { useSWRConfig } from "swr";
917
import SettingsIcon from "@/assets/icons/SettingsIcon";
1018
import { Account } from "@/interfaces/Account";
@@ -13,18 +21,23 @@ type Props = {
1321
account: Account;
1422
};
1523

16-
export default function NetworkSettingsTab({ account }: Props) {
24+
export default function NetworkSettingsTab({ account }: Readonly<Props>) {
1725
const { mutate } = useSWRConfig();
18-
const saveRequest = useApiCall<Account>("/accounts/" + account.id);
26+
const saveRequest = useApiCall<Account>("/accounts/" + account.id, true);
1927

2028
const [routingPeerDNSSetting, setRoutingPeerDNSSetting] = useState(
2129
account.settings.routing_peer_dns_resolution_enabled,
2230
);
31+
const [customDNSDomain, setCustomDNSDomain] = useState(
32+
account.settings.dns_domain || "",
33+
);
2334

24-
const toggleSetting = async (toggle: boolean) => {
35+
const toggleNetworkDNSSetting = async (toggle: boolean) => {
2536
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+
}.`,
2841
promise: saveRequest
2942
.put({
3043
id: account.id,
@@ -37,10 +50,43 @@ export default function NetworkSettingsTab({ account }: Props) {
3750
setRoutingPeerDNSSetting(toggle);
3851
mutate("/accounts");
3952
}),
40-
loadingMessage: "Saving the network settings...",
53+
loadingMessage: "Updating DNS wildcard setting...",
4154
});
4255
};
4356

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+
4490
return (
4591
<Tabs.Content value={"networks"}>
4692
<div className={"p-default py-6 max-w-2xl"}>
@@ -51,36 +97,80 @@ export default function NetworkSettingsTab({ account }: Props) {
5197
icon={<SettingsIcon size={13} />}
5298
/>
5399
<Breadcrumbs.Item
54-
href={"/settings#network"}
55-
label={"Network"}
100+
href={"/settings?tab=networks"}
101+
label={"Networks"}
56102
icon={<NetworkIcon size={14} />}
57103
active
58104
/>
59105
</Breadcrumbs>
60106
<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>
62117
</div>
63118

64119
<div className={"flex flex-col gap-6 w-full mt-8"}>
65120
<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"
81124
}
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>
83146
</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+
/>
84174
</div>
85175
</div>
86176
</Tabs.Content>

0 commit comments

Comments
 (0)