diff --git a/package-lock.json b/package-lock.json index 3174f51f..22c89d53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,7 @@ "ip-cidr": "^3.1.0", "js-cookie": "^3.0.5", "lodash": "^4.17.21", - "lucide-react": "^0.479.0", + "lucide-react": "^0.481.0", "next": "^14.2.28", "next-themes": "^0.2.1", "punycode": "^2.3.1", @@ -6621,9 +6621,9 @@ "license": "ISC" }, "node_modules/lucide-react": { - "version": "0.479.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.479.0.tgz", - "integrity": "sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==", + "version": "0.481.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.481.0.tgz", + "integrity": "sha512-NrvUDNFwgLIvHiwTEq9boa5Kiz1KdUT8RJ+wmNijwxdn9U737Fw42c43sRxJTMqhL+ySHpGRVCWpwiF+abrEjw==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/package.json b/package.json index c0278368..4a48dc17 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "ip-cidr": "^3.1.0", "js-cookie": "^3.0.5", "lodash": "^4.17.21", - "lucide-react": "^0.479.0", + "lucide-react": "^0.481.0", "next": "^14.2.28", "next-themes": "^0.2.1", "punycode": "^2.3.1", diff --git a/src/app/(dashboard)/peer/page.tsx b/src/app/(dashboard)/peer/page.tsx index d1724d56..d80a31fb 100644 --- a/src/app/(dashboard)/peer/page.tsx +++ b/src/app/(dashboard)/peer/page.tsx @@ -157,20 +157,19 @@ const PeerGeneralInformation = () => { * Detect if there are changes in the peer information, if there are changes, then enable the save button. */ const { hasChanges, updateRef: updateHasChangedRef } = useHasChanges([ - name, ssh, selectedGroups, loginExpiration, inactivityExpiration, ]); - const updatePeer = async () => { + const updatePeer = async (newName?: string) => { let batchCall: Promise[] = []; const groupCalls = getAllGroupCalls(); if (permission.peers.update) { const updateRequest = update({ - name, + name: newName ?? name, ssh, loginExpiration, inactivityExpiration, @@ -187,7 +186,6 @@ const PeerGeneralInformation = () => { mutate("/peers/" + peer.id); mutate("/groups"); updateHasChangedRef([ - name, ssh, selectedGroups, loginExpiration, @@ -229,8 +227,10 @@ const PeerGeneralInformation = () => { { - setName(newName); - setShowEditNameModal(false); + updatePeer(newName).then(() => { + setName(newName); + setShowEditNameModal(false); + }); }} peer={peer} initialName={name} diff --git a/src/app/(dashboard)/settings/page.tsx b/src/app/(dashboard)/settings/page.tsx index 8b7329b8..956f6907 100644 --- a/src/app/(dashboard)/settings/page.tsx +++ b/src/app/(dashboard)/settings/page.tsx @@ -6,6 +6,7 @@ import { AlertOctagonIcon, FolderGit2Icon, LockIcon, + MonitorSmartphoneIcon, NetworkIcon, ShieldIcon, } from "lucide-react"; @@ -16,6 +17,7 @@ import { useLoggedInUser } from "@/contexts/UsersProvider"; import PageContainer from "@/layouts/PageContainer"; import { useAccount } from "@/modules/account/useAccount"; import AuthenticationTab from "@/modules/settings/AuthenticationTab"; +import ClientSettingsTab from "@/modules/settings/ClientSettingsTab"; import DangerZoneTab from "@/modules/settings/DangerZoneTab"; import GroupsTab from "@/modules/settings/GroupsTab"; import NetworkSettingsTab from "@/modules/settings/NetworkSettingsTab"; @@ -63,6 +65,10 @@ export default function NetBirdSettings() { Networks + + + Clients + )} @@ -77,6 +83,7 @@ export default function NetBirdSettings() { {account && } {account && } {account && } + {account && } {account && } diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index dde62ed5..00e88867 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -8,6 +8,7 @@ interface Props extends React.HTMLAttributes, BadgeVariants { children: React.ReactNode; className?: string; useHover?: boolean; + disabled?: boolean; } const variants = cva("", { @@ -53,6 +54,7 @@ export default function Badge({ className, variant = "blue", useHover = false, + disabled = false, ...props }: Readonly) { return ( @@ -61,6 +63,7 @@ export default function Badge({ "relative z-10 cursor-inherit whitespace-nowrap rounded-md text-[12px] py-1.5 px-3 font-normal flex gap-1.5 items-center justify-center transition-all", className, variants({ variant, hover: useHover ? variant : "none" }), + disabled && "cursor-not-allowed opacity-50 select-none", )} {...props} > diff --git a/src/components/ui/InputDomain.tsx b/src/components/ui/InputDomain.tsx index b17d9cea..5393dac2 100644 --- a/src/components/ui/InputDomain.tsx +++ b/src/components/ui/InputDomain.tsx @@ -14,6 +14,8 @@ type Props = { onError?: (error: boolean) => void; error?: string; disabled?: boolean; + preventLeadingAndTrailingDots?: boolean; + allowWildcard?: boolean; }; enum ActionType { ADD = "ADD", @@ -40,6 +42,8 @@ export default function InputDomain({ onRemove, onError, disabled, + preventLeadingAndTrailingDots, + allowWildcard = true, }: Readonly) { const [name, setName] = useState(value?.name || ""); @@ -52,7 +56,11 @@ export default function InputDomain({ if (name == "") { return ""; } - const valid = validator.isValidDomain(name); + const valid = validator.isValidDomain(name, { + allowOnlyTld: true, + allowWildcard, + preventLeadingAndTrailingDots, + }); if (!valid) { return "Please enter a valid domain, e.g. example.com or intra.example.com"; } diff --git a/src/hooks/useRedirect.tsx b/src/hooks/useRedirect.tsx index 4be09fe6..0a67082b 100644 --- a/src/hooks/useRedirect.tsx +++ b/src/hooks/useRedirect.tsx @@ -16,9 +16,8 @@ export const useRedirect = ( const intervalRef = useRef(null); useEffect(() => { - // If redirect is disabled or the url is already in the callback urls or the url is the current path then do not redirect - if (!enable || callBackUrls.current.includes(url) || url === currentPath) - return; + // If redirect is disabled or the url is already in the callback urls then do not redirect + if (!enable || callBackUrls.current.includes(url)) return; const performRedirect = () => { if (!isRedirecting.current) { diff --git a/src/interfaces/Account.ts b/src/interfaces/Account.ts index 688dfb84..ed4d5e29 100644 --- a/src/interfaces/Account.ts +++ b/src/interfaces/Account.ts @@ -19,5 +19,6 @@ export interface Account { regular_users_view_blocked: boolean; routing_peer_dns_resolution_enabled: boolean; dns_domain: string; + lazy_connection_enabled: boolean; }; } diff --git a/src/modules/access-control/table/AccessControlPostureCheckCell.tsx b/src/modules/access-control/table/AccessControlPostureCheckCell.tsx index d3a96cd1..01167920 100644 --- a/src/modules/access-control/table/AccessControlPostureCheckCell.tsx +++ b/src/modules/access-control/table/AccessControlPostureCheckCell.tsx @@ -2,12 +2,17 @@ import Badge from "@components/Badge"; import { IconCirclePlus } from "@tabler/icons-react"; import { ShieldCheck } from "lucide-react"; import React from "react"; +import { usePermissions } from "@/contexts/PermissionsProvider"; import { Policy } from "@/interfaces/Policy"; type Props = { policy: Policy; }; export default function AccessControlPostureCheckCell({ policy }: Props) { + const { permission } = usePermissions(); + + const isDisabled = !permission.policies.create || !permission.policies.update; + return policy.source_posture_checks && policy.source_posture_checks.length > 0 ? (
@@ -18,7 +23,14 @@ export default function AccessControlPostureCheckCell({ policy }: Props) {
) : (
- + { + if (isDisabled) e.stopPropagation(); + }} + disabled={isDisabled} + > Add Posture Check diff --git a/src/modules/access-control/table/AccessControlTable.tsx b/src/modules/access-control/table/AccessControlTable.tsx index dcf7d714..b5b21536 100644 --- a/src/modules/access-control/table/AccessControlTable.tsx +++ b/src/modules/access-control/table/AccessControlTable.tsx @@ -8,6 +8,7 @@ import DataTableRefreshButton from "@components/table/DataTableRefreshButton"; import { DataTableRowsPerPage } from "@components/table/DataTableRowsPerPage"; import GetStartedTest from "@components/ui/GetStartedTest"; import type { ColumnDef, SortingState } from "@tanstack/react-table"; +import { removeAllSpaces } from "@utils/helpers"; import { ExternalLinkIcon, PlusCircle } from "lucide-react"; import { usePathname } from "next/navigation"; import React, { useState } from "react"; @@ -37,16 +38,20 @@ type Props = { export const AccessControlTableColumns: ColumnDef[] = [ { - accessorKey: "name", + id: "name", + accessorFn: (row) => removeAllSpaces(row?.name), header: ({ column }) => { return Name; }, sortingFn: "text", + filterFn: "fuzzy", cell: ({ cell }) => , }, { - accessorKey: "description", + id: "description", + accessorFn: (row) => removeAllSpaces(row?.description), sortingFn: "text", + filterFn: "fuzzy", }, { id: "enabled", diff --git a/src/modules/dns-nameservers/NameserverModal.tsx b/src/modules/dns-nameservers/NameserverModal.tsx index 3ddaa9d9..18773d53 100644 --- a/src/modules/dns-nameservers/NameserverModal.tsx +++ b/src/modules/dns-nameservers/NameserverModal.tsx @@ -371,6 +371,8 @@ export function NameserverModalContent({ {domains.map((domain, i) => { return ( diff --git a/src/modules/groups/useGroupHelper.tsx b/src/modules/groups/useGroupHelper.tsx index e617d98e..0bf7c5df 100644 --- a/src/modules/groups/useGroupHelper.tsx +++ b/src/modules/groups/useGroupHelper.tsx @@ -1,5 +1,5 @@ import { useApiCall } from "@utils/api"; -import { isEmpty } from "lodash"; +import { isEmpty, orderBy } from "lodash"; import { useMemo, useState } from "react"; import { useSWRConfig } from "swr"; import { useGroups } from "@/contexts/GroupsProvider"; @@ -20,13 +20,13 @@ export default function useGroupHelper({ initial = [], peer }: Props) { const initialGroups = useMemo(() => { if (!initial) return []; const isArrayOfStrings = initial.every((item) => typeof item === "string"); - if (!isArrayOfStrings) return initial as Group[]; + if (!isArrayOfStrings) return orderBy(initial as Group[], ["name"]); const foundGroups = initial .map((id) => { return groups?.find((g) => g.id === id); }) .filter((g) => g !== undefined) as Group[]; - return foundGroups ?? []; + return orderBy(foundGroups, ["name"]) ?? []; }, [groups, initial]); const [selectedGroups, setSelectedGroups] = useState(initialGroups); diff --git a/src/modules/networks/resources/NetworkResourceModal.tsx b/src/modules/networks/resources/NetworkResourceModal.tsx index 06a2c5c5..41e50566 100644 --- a/src/modules/networks/resources/NetworkResourceModal.tsx +++ b/src/modules/networks/resources/NetworkResourceModal.tsx @@ -173,7 +173,7 @@ export function ResourceModalContent({
- + Add this resource to groups and use them as destinations when creating policies diff --git a/src/modules/posture-checks/table/PostureCheckTable.tsx b/src/modules/posture-checks/table/PostureCheckTable.tsx index b04c0b54..91321eff 100644 --- a/src/modules/posture-checks/table/PostureCheckTable.tsx +++ b/src/modules/posture-checks/table/PostureCheckTable.tsx @@ -147,7 +147,9 @@ export default function PostureCheckTable({