Compare commits

...
16 Commits
Author SHA1 Message Date
Maycon Santos d9fb379abf Enable connect buttons (#493) 2025-10-06 16:23:00 -03:00
Eduard Gert 831673d0d6 Sync with cloud (#491)
implements a "Sync with cloud" functionality that includes various UI improvements, code refactoring, and component extractions. The changes focus on enhancing the user interface, improving code organization, and adding new features for remote access and activity tracking.

- Refactors inline components into reusable shared components
- Adds new activity tracking for group operations
- Updates remote access configuration and UI components
- Enhances styling and layout for better user experience
2025-10-03 14:37:11 +02:00
Maycon Santos bc4aac10aa Add browser client support (#490)
* Sync wasm rdp and ssh client

* sync package-lock

* remove msp ref

* add ephemeral info
2025-10-02 00:41:08 +02:00
Eduard Gert 38e14a6c64 Allow delete groups issued by jwt (#487) 2025-09-10 14:41:19 +02:00
Maycon Santos b79c6615b4 Add user approval feature (#486)
implements a user approval feature that allows administrators to manually approve new users before they can access the system. The feature adds approval workflow controls and error handling for blocked/pending users.

Adds user approval toggle in authentication settings
Implements approve/reject actions for pending users in the users table
Creates error page for blocked/pending approval scenarios
2025-09-02 15:25:30 +02:00
hakansa 5d4e491611 Add skip_auto_apply feature to exit nodes and update related components (#484) 2025-08-20 13:11:45 +02:00
Eduard Gert 9b1f920863 Update dependencies (#483) 2025-08-19 17:32:22 +02:00
Eduard Gert 7c7f0a0f10 Add network range (#482) 2025-08-13 14:18:59 +02:00
braginini 76541c701c Update LICENSE 2025-08-05 11:19:13 +02:00
Eduard Gert d2046fee21 Add resource search, unidirectional policies for all/icmp and dns0 template (#479) 2025-07-30 17:01:49 +02:00
Eduard Gert 8e2cbe1d2a Add support for port ranges (#475) 2025-06-20 10:26:53 +02:00
Eduard Gert 8a08583225 Do not redirect on same page (#471) 2025-06-05 19:30:25 +02:00
Eduard GertandViktor Liu 1defac4e34 Update wording for dns domain, macOS and Windows install steps (#470)
* Update wording for dns domain, macOS and Windows install steps

* Update src/modules/settings/NetworkSettingsTab.tsx

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
2025-06-05 13:16:42 +02:00
Eduard Gert fa68f98cd0 Remove permission for add peer button (#469) 2025-06-05 13:12:38 +02:00
Eduard Gert 3f6e4c4e4f Add lazy connection setting (#465) 2025-06-04 11:54:18 +02:00
Eduard GertandMaycon Santos 0e2661caea Merge cloud changes to public (#462)
* Add changes from dashboard cloud

* Add changes from dashboard cloud

* Update next.js version

* Small formatting changes

* remove unknown permission check

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2025-05-05 15:30:28 +02:00
277 changed files with 15824 additions and 3537 deletions

No files matched your search

+28 -2
View File
@@ -28,6 +28,33 @@ jobs:
- run: echo '{}' > .local-config.json
- name: Download IronRDP release TS files
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "*.ts"
out-file-path: 'public/ironrdp-pkg'
- name: Download IronRDP release JS files
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "*.js"
out-file-path: 'public/ironrdp-pkg'
- name: Download IronRDP release WASM file
uses: robinraju/release-downloader@v1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: netbirdio/IronRDP
latest: true
fileName: "ironrdp_web_bg.wasm"
out-file-path: 'public/ironrdp-pkg'
- name: Build
run: npm run build
-
@@ -44,7 +71,6 @@ jobs:
images: ${{ env.IMAGE_NAME }}
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.NB_DOCKER_USER }}
@@ -55,7 +81,7 @@ jobs:
with:
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
push: true
platforms: linux/amd64,linux/arm64,linux/arm
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+5
View File
@@ -41,3 +41,8 @@ next-env.d.ts
.configs/.staging-config.json
.configs/.temp-config.json
.configs
/public/ironrdp-pkg/
/public/netbird.wasm
.idea
src/.local-config*
+655 -7
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -12,5 +12,6 @@
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS",
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID"
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID",
"googleTagManagerID": "$NETBIRD_GOOGLE_TAG_MANAGER_ID"
}
+4
View File
@@ -3,6 +3,10 @@ server {
listen [::]:80 default_server;
root /usr/share/nginx/html;
location = /netbird.wasm {
root /usr/share/nginx/html;
default_type application/wasm;
}
location / {
try_files $uri $uri.html $uri/ =404;
+2 -1
View File
@@ -58,13 +58,14 @@ export NETBIRD_MGMT_API_ENDPOINT=$(echo $NETBIRD_MGMT_API_ENDPOINT | sed -E 's/(
export NETBIRD_MGMT_GRPC_API_ENDPOINT=${NETBIRD_MGMT_GRPC_API_ENDPOINT}
export NETBIRD_HOTJAR_TRACK_ID=${NETBIRD_HOTJAR_TRACK_ID}
export NETBIRD_GOOGLE_ANALYTICS_ID=${NETBIRD_GOOGLE_ANALYTICS_ID}
export NETBIRD_GOOGLE_TAG_MANAGER_ID=${NETBIRD_GOOGLE_TAG_MANAGER_ID}
export NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
export NETBIRD_DRAG_QUERY_PARAMS=${NETBIRD_DRAG_QUERY_PARAMS:-false}
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
# replace ENVs in the config
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS".tmpl > "$OIDC_TRUSTED_DOMAINS"
+1
View File
@@ -101,6 +101,7 @@ http {
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/wasm
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
+954 -312
View File
File diff suppressed because it is too large. Load diff
+14 -5
View File
@@ -40,7 +40,11 @@
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-window": "^1.8.8",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"autoprefixer": "^10",
"chart.js": "^4.4.8",
"chroma-js": "^3.1.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
@@ -48,21 +52,21 @@
"date-fns": "^2.30.0",
"dayjs": "^1.11.10",
"eslint": "^8",
"eslint-config-next": "13.5.5",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"flowbite": "^1.8.1",
"flowbite-react": "^0.6.4",
"framer-motion": "^10.16.4",
"ip-cidr": "^3.1.0",
"js-cookie": "^3.0.5",
"lodash": "^4.17.21",
"lucide-react": "^0.460.0",
"next": "13.5.7",
"lucide-react": "^0.481.0",
"next": "^14.2.28",
"next-themes": "^0.2.1",
"punycode": "^2.3.1",
"react": "^18",
"react": "^18.3.1",
"react-day-picker": "^8.9.1",
"react-dom": "^18",
"react-dom": "^18.3.1",
"react-ga4": "^2.1.0",
"react-hot-toast": "^2.4.1",
"react-hotjar": "^6.2.0",
@@ -74,9 +78,14 @@
"swr": "^2.2.4",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"timescape": "^0.7.1",
"typescript": "^5"
},
"devDependencies": {
"@faker-js/faker": "^9.5.1",
"@types/chroma-js": "^3.1.1",
"@types/js-cookie": "^3.0.6",
"eslint-config-next": "^14.2.28",
"cypress": "^13.13.0",
"postcss": "^8",
"prettier": "3.0.3",
+575
View File
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,10 @@
"use client";
import FullScreenLoading from "@components/ui/FullScreenLoading";
import { useRedirect } from "@hooks/useRedirect";
import React from "react";
export default function Redirect() {
useRedirect("/events/audit");
return <FullScreenLoading height={"auto"} />;
}
+7 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import AccessControlIcon from "@/assets/icons/AccessControlIcon";
import GroupsProvider from "@/contexts/GroupsProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import PoliciesProvider from "@/contexts/PoliciesProvider";
import { Policy } from "@/interfaces/Policy";
import PageContainer from "@/layouts/PageContainer";
@@ -19,6 +20,8 @@ const AccessControlTable = lazy(
() => import("@/modules/access-control/table/AccessControlTable"),
);
export default function AccessControlPage() {
const { permission } = usePermissions();
const { data: policies, isLoading } = useFetchApi<Policy[]>("/policies");
const { ref: headingRef, portalTarget } =
@@ -53,7 +56,10 @@ export default function AccessControlPage() {
</Paragraph>
</div>
<RestrictedAccess page={"Access Control"}>
<RestrictedAccess
page={"Access Control"}
hasAccess={permission.policies.read}
>
<PoliciesProvider>
<Suspense fallback={<SkeletonTable />}>
<AccessControlTable
+7 -1
View File
@@ -10,6 +10,7 @@ import useFetchApi from "@utils/api";
import { ExternalLinkIcon, ServerIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import DNSIcon from "@/assets/icons/DNSIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { NameserverGroup } from "@/interfaces/Nameserver";
import PageContainer from "@/layouts/PageContainer";
@@ -18,6 +19,8 @@ const NameserverGroupTable = lazy(
);
export default function NameServers() {
const { permission } = usePermissions();
const { data: nameserverGroups, isLoading } =
useFetchApi<NameserverGroup[]>("/dns/nameservers");
@@ -57,7 +60,10 @@ export default function NameServers() {
</Paragraph>
</div>
<RestrictedAccess page={"Nameservers"}>
<RestrictedAccess
page={"Nameservers"}
hasAccess={permission.nameservers.read}
>
<Suspense fallback={<SkeletonTable />}>
<NameserverGroupTable
nameserverGroups={nameserverGroups}
+7 -2
View File
@@ -17,6 +17,7 @@ import React from "react";
import Skeleton from "react-loading-skeleton";
import { useSWRConfig } from "swr";
import DNSIcon from "@/assets/icons/DNSIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { useHasChanges } from "@/hooks/useHasChanges";
import { Group } from "@/interfaces/Group";
import { NameserverSettings } from "@/interfaces/NameserverSettings";
@@ -25,6 +26,8 @@ import useGroupHelper from "@/modules/groups/useGroupHelper";
import { useGroupIdsToGroups } from "@/modules/groups/useGroupIdsToGroups";
export default function NameServerSettings() {
const { permission } = usePermissions();
const { data: settings, isLoading } =
useFetchApi<NameserverSettings>("/dns/settings");
@@ -61,7 +64,7 @@ export default function NameServerSettings() {
</InlineLink>
in our documentation.
</Paragraph>
<RestrictedAccess page={"DNS Settings"}>
<RestrictedAccess page={"DNS Settings"} hasAccess={permission.dns.read}>
{!isLoading && initialDNSGroups !== undefined ? (
<SettingDisabledManagementGroups initialGroups={initialDNSGroups} />
) : (
@@ -86,6 +89,7 @@ const SettingDisabledManagementGroups = ({
}) => {
const settingRequest = useApiCall<NameserverSettings>("/dns/settings");
const { mutate } = useSWRConfig();
const { permission } = usePermissions();
const [selectedGroups, setSelectedGroups, { save: saveGroups }] =
useGroupHelper({
@@ -124,6 +128,7 @@ const SettingDisabledManagementGroups = ({
dataCy={"dns-groups-selector"}
onChange={setSelectedGroups}
values={selectedGroups}
disabled={!permission.dns.update}
/>
</div>
<div
@@ -135,7 +140,7 @@ const SettingDisabledManagementGroups = ({
variant={"primary"}
size={"sm"}
onClick={saveSettings}
disabled={!hasChanges}
disabled={!hasChanges || !permission.dns.update}
data-cy={"save-changes"}
>
Save Changes
@@ -0,0 +1,8 @@
import { globalMetaTitle } from "@utils/meta";
import type { Metadata } from "next";
import BlankLayout from "@/layouts/BlankLayout";
export const metadata: Metadata = {
title: `Audit Events - Activity - ${globalMetaTitle}`,
};
export default BlankLayout;
@@ -6,15 +6,19 @@ import Paragraph from "@components/Paragraph";
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
import { usePortalElement } from "@hooks/usePortalElement";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import { ExternalLinkIcon, LogsIcon } from "lucide-react";
import React from "react";
import ActivityIcon from "@/assets/icons/ActivityIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { ActivityEvent } from "@/interfaces/ActivityEvent";
import PageContainer from "@/layouts/PageContainer";
import ActivityTable from "@/modules/activity/ActivityTable";
export default function Activity() {
const { data: events, isLoading } = useFetchApi<ActivityEvent[]>("/events");
const { permission } = usePermissions();
const { data: events, isLoading } =
useFetchApi<ActivityEvent[]>("/events/audit");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
@@ -24,30 +28,31 @@ export default function Activity() {
<div className={"p-default py-6"}>
<Breadcrumbs>
<Breadcrumbs.Item
href={"/activity"}
label={"Activity"}
disabled={true}
icon={<ActivityIcon size={13} />}
/>
<Breadcrumbs.Item
href={"/events/audit"}
label={"Audit Events"}
icon={<LogsIcon size={18} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Activity Events</h1>
<Paragraph>
Here you can see all the account and network activity events.
</Paragraph>
<h1 ref={headingRef}>Audit Events</h1>
<Paragraph>Here you can see all the audit activity events.</Paragraph>
<Paragraph>
Learn more about{" "}
<InlineLink
href={
"https://docs.netbird.io/how-to/monitor-system-and-network-activity"
}
href={"https://docs.netbird.io/how-to/audit-events-logging"}
target={"_blank"}
>
Activity Events
Audit Events
<ExternalLinkIcon size={12} />
</InlineLink>
in our documentation.
</Paragraph>
</div>
<RestrictedAccess page={"Activity"}>
<RestrictedAccess page={"Activity"} hasAccess={permission.events.read}>
<ActivityTable
events={events}
isLoading={isLoading}
+3 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import PeersProvider from "@/contexts/PeersProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import RoutesProvider from "@/contexts/RoutesProvider";
import { Route } from "@/interfaces/Route";
import PageContainer from "@/layouts/PageContainer";
@@ -21,6 +22,7 @@ const NetworkRoutesTable = lazy(
);
export default function NetworkRoutes() {
const { permission } = usePermissions();
const { data: routes, isLoading } = useFetchApi<Route[]>("/routes");
const groupedRoutes = useGroupedRoutes({ routes });
@@ -59,7 +61,7 @@ export default function NetworkRoutes() {
</Paragraph>
</div>
<RestrictedAccess>
<RestrictedAccess hasAccess={permission.routes.read}>
<Suspense fallback={<SkeletonTable />}>
<NetworkRoutesTable
isLoading={isLoading}
+14 -11
View File
@@ -21,7 +21,7 @@ import { useSearchParams } from "next/navigation";
import React, { useMemo, useState } from "react";
import { useSWRConfig } from "swr";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import { NetworkInformationSquare } from "@/modules/networks/misc/NetworkInformationSquare";
@@ -48,7 +48,8 @@ export default function NetworkDetailPage() {
}
function NetworkOverview({ network }: Readonly<{ network: Network }>) {
const { isUser } = useLoggedInUser();
const { permission } = usePermissions();
const [networkModal, setNetworkModal] = useState(false);
const { mutate } = useSWRConfig();
@@ -64,7 +65,7 @@ function NetworkOverview({ network }: Readonly<{ network: Network }>) {
<Breadcrumbs.Item
href={"/networks"}
label={"Networks"}
disabled={isUser}
disabled={!permission.networks.read}
icon={<NetworkRoutesIcon size={13} />}
/>
<Breadcrumbs.Item
@@ -87,14 +88,16 @@ function NetworkOverview({ network }: Readonly<{ network: Network }>) {
size={"lg"}
description={network.description}
/>
<button
className={
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
}
onClick={() => setNetworkModal(true)}
>
<PencilLineIcon size={18} />
</button>
{permission.networks.update && (
<button
className={
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
}
onClick={() => setNetworkModal(true)}
>
<PencilLineIcon size={18} />
</button>
)}
<NetworkModal
open={networkModal}
setOpen={setNetworkModal}
+5 -3
View File
@@ -10,12 +10,14 @@ import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React, { Suspense } from "react";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import NetworksTable from "@/modules/networks/table/NetworksTable";
export default function Networks() {
const { data: networks, isLoading } = useFetchApi<Network[]>("/networks");
const { permission } = usePermissions();
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
@@ -31,8 +33,8 @@ export default function Networks() {
</Breadcrumbs>
<h1 ref={headingRef}>Networks</h1>
<Paragraph>
Networks allow you to access internal resources in LANs and VPCs without
installing NetBird on every machine.
Networks allow you to access internal resources in LANs and VPCs
without installing NetBird on every machine.
</Paragraph>
<Paragraph>
Learn more about
@@ -47,7 +49,7 @@ export default function Networks() {
</Paragraph>
</div>
<RestrictedAccess>
<RestrictedAccess hasAccess={permission.networks.read}>
<Suspense fallback={<SkeletonTable />}>
<NetworksTable
data={networks}
Loaded 20 of 277 files, more files were not shown because too many files have changed in this diff. Show more