mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
61
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d2deb5bd | ||
|
|
69582b2867 | ||
|
|
4e227d6efb | ||
|
|
2ad96f2aee | ||
|
|
22b28ff189 | ||
|
|
c6341e000f | ||
|
|
750f660bcc | ||
|
|
ea148545e8 | ||
|
|
d2febbf27b | ||
|
|
615b4487ad | ||
|
|
a7c7800916 | ||
|
|
3d51e0893e | ||
|
|
d7d44b5817 | ||
|
|
f67f39b68b | ||
|
|
d2bc7a1f57 | ||
|
|
818ba5daa4 | ||
|
|
3a30f76629 | ||
|
|
34dc21c89d | ||
|
|
2e37703622 | ||
|
|
8aec338c43 | ||
|
|
f4f0c240fd | ||
|
|
04e22a3c7e | ||
|
|
54ef076303 | ||
|
|
92676b6c38 | ||
|
|
3affa8908f | ||
|
|
52fd984912 | ||
|
|
83e3159ee4 | ||
|
|
bf81aeb02d | ||
|
|
b058e66e32 | ||
|
|
8d6b617cbd | ||
|
|
47db655e9f | ||
|
|
0661cbf9f4 | ||
|
|
240a96fa8b | ||
|
|
43bc069a49 | ||
|
|
936de0f4f3 | ||
|
|
d81b75a946 | ||
|
|
a632eeeef0 | ||
|
|
e2219aeea0 | ||
|
|
63f4c69eb4 | ||
|
|
b1af256296 | ||
|
|
4027894a2e | ||
|
|
af90792595 | ||
|
|
9a401733b3 | ||
|
|
07b6895380 | ||
|
|
9e2e38764e | ||
|
|
d9fb379abf | ||
|
|
831673d0d6 | ||
|
|
bc4aac10aa | ||
|
|
38e14a6c64 | ||
|
|
b79c6615b4 | ||
|
|
5d4e491611 | ||
|
|
9b1f920863 | ||
|
|
7c7f0a0f10 | ||
|
|
76541c701c | ||
|
|
d2046fee21 | ||
|
|
8e2cbe1d2a | ||
|
|
8a08583225 | ||
|
|
1defac4e34 | ||
|
|
fa68f98cd0 | ||
|
|
3f6e4c4e4f | ||
|
|
0e2661caea |
No files matched your search
@@ -2,7 +2,6 @@ name: build and push
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "feature/**"
|
||||
- main
|
||||
tags:
|
||||
- "**"
|
||||
@@ -20,7 +19,7 @@ jobs:
|
||||
- name: setup-node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -28,8 +27,46 @@ 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: Get version from tag
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "version=development" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
env:
|
||||
NEXT_PUBLIC_DASHBOARD_VERSION: ${{ steps.version.outputs.version }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
@@ -44,7 +81,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 +91,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 }}
|
||||
@@ -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*
|
||||
@@ -1,7 +1,7 @@
|
||||
## Contributor License Agreement
|
||||
|
||||
This Contributor License Agreement (referred to as the "Agreement") is entered into by the individual
|
||||
submitting this Agreement and NetBird GmbH, c/o Max-Beer-StraĂźe 2-4 MĂĽnzstraĂźe 12 10178 Berlin, Germany,
|
||||
submitting this Agreement and NetBird GmbH, BrunnenstraĂźe 196, 10119 Berlin, Germany,
|
||||
referred to as "NetBird" (collectively, the "Parties"). The Agreement outlines the terms and conditions
|
||||
under which NetBird may utilize software contributions provided by the Contributor for inclusion in
|
||||
its software development projects. By submitting this Agreement, the Contributor confirms their acceptance
|
||||
|
||||
@@ -10,6 +10,7 @@ See [NetBird repo](https://github.com/netbirdio/netbird)
|
||||
|
||||
The purpose of this project is simple - make it easy to manage VPN built with [NetBird](https://github.com/netbirdio/netbird).
|
||||
The dashboard makes it possible to:
|
||||
|
||||
- track the status of your peers
|
||||
- remove peers
|
||||
- manage Setup Keys (to authenticate new peers)
|
||||
@@ -17,23 +18,25 @@ The dashboard makes it possible to:
|
||||
- define access controls
|
||||
|
||||
## Some Screenshots
|
||||
|
||||
<img src="./src/assets/screenshots/peers.png" alt="peers"/>
|
||||
<img src="./src/assets/screenshots/add-peer.png" alt="add-peer"/>
|
||||
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- NextJS
|
||||
- ReactJS
|
||||
- Tailwind CSS
|
||||
- [React Flow](https://reactflow.dev/) for the Control Center
|
||||
- Auth0
|
||||
- Nginx
|
||||
- Docker
|
||||
- Let's Encrypt
|
||||
|
||||
## How to run
|
||||
|
||||
Disclaimer. We believe that proper user management system is not a trivial task and requires quite some effort to make it right. Therefore we decided to
|
||||
use Auth0 service that covers all our needs (user management, social login, JTW for the management API).
|
||||
use Auth0 service that covers all our needs (user management, social login, JWT for the management API).
|
||||
Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
|
||||
|
||||
1. Install [Docker](https://docs.docker.com/get-docker/)
|
||||
@@ -42,9 +45,9 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
|
||||
|
||||
`AUTH0_DOMAIN` `AUTH0_CLIENT_ID` `AUTH0_AUDIENCE`
|
||||
|
||||
To obtain these, please use [Auth0 React SDK Guide](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0) up until "Configure Allowed Web Origins"
|
||||
To obtain these, please use [Auth0 React SDK Guide](https://auth0.com/docs/quickstart/spa/react) up until "Configure Allowed Web Origins"
|
||||
|
||||
4. NetBird UI Dashboard uses NetBirds Management Service HTTP API, so setting `NETBIRD_MGMT_API_ENDPOINT` is required. Most likely it will be `http://localhost:33071` if you are hosting Management API on the same server.
|
||||
4. NetBird UI Dashboard uses NetBird's Management Service HTTP API, so setting `NETBIRD_MGMT_API_ENDPOINT` is required. Most likely it will be `http://localhost:33071` if you are hosting Management API on the same server.
|
||||
5. Run docker container without SSL (Let's Encrypt):
|
||||
|
||||
```shell
|
||||
@@ -53,9 +56,10 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
|
||||
-e AUTH0_DOMAIN=<SET YOUR AUTH DOMAIN> \
|
||||
-e AUTH0_CLIENT_ID=<SET YOUR CLIENT ID> \
|
||||
-e AUTH0_AUDIENCE=<SET YOUR AUDIENCE> \
|
||||
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMETN API URL> \
|
||||
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMENT API URL> \
|
||||
netbirdio/dashboard:main
|
||||
```
|
||||
|
||||
6. Run docker container with SSL (Let's Encrypt):
|
||||
|
||||
```shell
|
||||
@@ -67,7 +71,7 @@ Auth0 so far is the only 3rd party dependency that can't be really self-hosted.
|
||||
-e AUTH0_DOMAIN=<SET YOUR AUTH DOMAIN> \
|
||||
-e AUTH0_CLIENT_ID=<SET YOUR CLEITN ID> \
|
||||
-e AUTH0_AUDIENCE=<SET YOUR AUDIENCE> \
|
||||
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMETN API URL> \
|
||||
-e NETBIRD_MGMT_API_ENDPOINT=<SET YOUR MANAGEMENT API URL> \
|
||||
netbirdio/dashboard:main
|
||||
```
|
||||
|
||||
@@ -83,11 +87,11 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
|
||||
You can start editing by modifying the code inside `src/..`
|
||||
The page auto-updates as you edit the file.
|
||||
|
||||
## How to migrate from old dashboard (v1)
|
||||
## How to migrate from old dashboard (v1)
|
||||
|
||||
The new dashboard comes with a new docker image `netbirdio/dashboard:main`.
|
||||
To migrate from the old dashboard (v1) `wiretrustee/dashboard:main` to the new one, please follow the steps below.
|
||||
|
||||
1. Stop the dashboard container `docker compose down dashboard`
|
||||
2. Replace the docker image name in your `docker-compose.yml` with `netbirdio/dashboard:main`
|
||||
3. Recreate the dashboard container `docker compose up -d --force-recreate dashboard`
|
||||
3. Recreate the dashboard container `docker compose up -d --force-recreate dashboard`
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"tag": "New",
|
||||
"text": "Custom DNS Zones for Private Network Resolution",
|
||||
"link": "https://netbird.io/knowledge-hub/custom-dns-zones",
|
||||
"linkText": "Read Release Article",
|
||||
"variant": "important",
|
||||
"isExternal": true,
|
||||
"closeable": true,
|
||||
"isCloudOnly": false
|
||||
}
|
||||
]
|
||||
+4
-2
@@ -12,5 +12,7 @@
|
||||
"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",
|
||||
"wasmPath": "$NETBIRD_WASM_PATH"
|
||||
}
|
||||
+20
-2
@@ -3,17 +3,35 @@ server {
|
||||
listen [::]:80 default_server;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
location = /netbird.wasm {
|
||||
root /usr/share/nginx/html;
|
||||
default_type application/wasm;
|
||||
}
|
||||
location = /ironrdp-pkg/ironrdp_web_bg.wasm {
|
||||
root /usr/share/nginx/html;
|
||||
default_type application/wasm;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
|
||||
add_header Last-Modified "";
|
||||
expires off;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
location = /404.html {
|
||||
internal;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
|
||||
add_header Last-Modified "";
|
||||
expires off;
|
||||
}
|
||||
}
|
||||
@@ -58,13 +58,99 @@ 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}
|
||||
export NETBIRD_WASM_PATH=${NETBIRD_WASM_PATH}
|
||||
export NETBIRD_CSP=${NETBIRD_CSP}
|
||||
export NETBIRD_CSP_CONNECT_SRC=${NETBIRD_CSP_CONNECT_SRC}
|
||||
export NETBIRD_DISABLE_CSP=${NETBIRD_DISABLE_CSP}
|
||||
|
||||
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
|
||||
|
||||
# Build CSP
|
||||
if [[ "${NETBIRD_DISABLE_CSP}" == "true" ]]; then
|
||||
echo "CSP disabled via NETBIRD_DISABLE_CSP"
|
||||
sed -i '/add_header Content-Security-Policy/d' /etc/nginx/http.d/default.conf
|
||||
else
|
||||
FIRST_PARTY_CSP="https://pkgs.netbird.io"
|
||||
FIRST_PARTY_CSP_CONNECT_SRC="$NETBIRD_CSP_CONNECT_SRC"
|
||||
THIRD_PARTY_CSP=""
|
||||
THIRD_PARTY_CSP_CONNECT_SRC="https://api.github.com/repos/netbirdio/netbird/releases/latest https://raw.githubusercontent.com/netbirdio/dashboard/"
|
||||
THIRD_PARTY_CSP_SCRIPT_SRC=""
|
||||
|
||||
CSP_DOMAINS=""
|
||||
CSP_DOMAINS_CONNECT_SRC=""
|
||||
|
||||
if [[ -n "${NETBIRD_CSP}" ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_CSP"
|
||||
fi
|
||||
|
||||
# Add AUTH_AUTHORITY to CSP
|
||||
if [[ -n "${AUTH_AUTHORITY}" ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS $AUTH_AUTHORITY"
|
||||
fi
|
||||
|
||||
# Add AUTH_AUDIENCE to CSP
|
||||
if [[ -n "${AUTH_AUDIENCE}" && "${AUTH_AUDIENCE}" != "none" && "${AUTH_AUDIENCE}" == *.* ]]; then
|
||||
if [[ "${AUTH_AUDIENCE}" == *"http://"* || "${AUTH_AUDIENCE}" == *"https://"* ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS $AUTH_AUDIENCE"
|
||||
else
|
||||
CSP_DOMAINS="$CSP_DOMAINS https://$AUTH_AUDIENCE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add NETBIRD_MGMT_API_ENDPOINT to CSP
|
||||
if [[ -n "${NETBIRD_MGMT_API_ENDPOINT}" ]]; then
|
||||
MGMT_HOST=$(echo "$NETBIRD_MGMT_API_ENDPOINT" | sed -E 's|https?://||' | cut -d'/' -f1)
|
||||
if [[ -n "$MGMT_HOST" ]]; then
|
||||
if [[ "$NETBIRD_MGMT_API_ENDPOINT" == https://* ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_MGMT_API_ENDPOINT"
|
||||
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC wss://$MGMT_HOST"
|
||||
elif [[ "$NETBIRD_MGMT_API_ENDPOINT" == http://* ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_MGMT_API_ENDPOINT"
|
||||
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC ws://$MGMT_HOST"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add LETSENCRYPT_DOMAIN to CSP
|
||||
if [[ -n "${LETSENCRYPT_DOMAIN}" && "${LETSENCRYPT_DOMAIN}" != "none" ]]; then
|
||||
if [[ "$LETSENCRYPT_DOMAIN" == *"localhost"* ]]; then
|
||||
CSP_DOMAINS="$CSP_DOMAINS http://$LETSENCRYPT_DOMAIN"
|
||||
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC ws://$LETSENCRYPT_DOMAIN ws://*.$LETSENCRYPT_DOMAIN"
|
||||
else
|
||||
CSP_DOMAINS="$CSP_DOMAINS https://$LETSENCRYPT_DOMAIN"
|
||||
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC wss://$LETSENCRYPT_DOMAIN wss://*.$LETSENCRYPT_DOMAIN"
|
||||
fi
|
||||
fi
|
||||
|
||||
CSP_CONNECT_SRC="$CSP_DOMAINS $CSP_DOMAINS_CONNECT_SRC $FIRST_PARTY_CSP $FIRST_PARTY_CSP_CONNECT_SRC $THIRD_PARTY_CSP $THIRD_PARTY_CSP_CONNECT_SRC"
|
||||
CSP_FRAME_SRC="$CSP_DOMAINS $FIRST_PARTY_CSP $THIRD_PARTY_CSP"
|
||||
CSP_SCRIPT_SRC="$CSP_DOMAINS $FIRST_PARTY_CSP $THIRD_PARTY_CSP $THIRD_PARTY_CSP_SCRIPT_SRC"
|
||||
|
||||
# Remove duplicates
|
||||
CSP_CONNECT_SRC=$(echo $CSP_CONNECT_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||
CSP_FRAME_SRC=$(echo $CSP_FRAME_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||
CSP_SCRIPT_SRC=$(echo $CSP_SCRIPT_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||
|
||||
# Update CSP in nginx config
|
||||
CSP_POLICY="default-src 'none'; connect-src 'self' $CSP_CONNECT_SRC; frame-src 'self' $CSP_FRAME_SRC; script-src 'self' 'wasm-unsafe-eval' $CSP_SCRIPT_SRC; font-src 'self'; img-src * data:; manifest-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;"
|
||||
CSP_HEADER="add_header Content-Security-Policy \"$CSP_POLICY\" always;"
|
||||
|
||||
# Escape sed special characters in replacement string (& | \)
|
||||
CSP_HEADER=$(printf '%s' "$CSP_HEADER" | sed -e 's/[\\&|]/\\&/g')
|
||||
|
||||
echo "CSP header: $CSP_HEADER"
|
||||
|
||||
# Replace CSP header in nginx config
|
||||
sed -i "s|add_header Content-Security-Policy \"[^\"]*\" always;|$CSP_HEADER|g" /etc/nginx/http.d/default.conf || {
|
||||
echo "Failed to replace CSP header"
|
||||
}
|
||||
fi
|
||||
|
||||
# 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 \$\$NETBIRD_WASM_PATH"
|
||||
|
||||
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
|
||||
envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS".tmpl > "$OIDC_TRUSTED_DOMAINS"
|
||||
@@ -72,4 +158,7 @@ for f in $(grep -R -l AUTH_SUPPORTED_SCOPES /usr/share/nginx/html); do
|
||||
cp "$f" "$f".copy
|
||||
envsubst "$ENV_STR" < "$f".copy > "$f"
|
||||
rm "$f".copy
|
||||
done
|
||||
done
|
||||
|
||||
# Reload nginx to pick up the updated CSP and config
|
||||
nginx -s reload
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,8 @@ const nextConfig = {
|
||||
reactStrictMode: false,
|
||||
env: {
|
||||
APP_ENV: process.env.APP_ENV || "production",
|
||||
NEXT_PUBLIC_DASHBOARD_VERSION:
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_VERSION || "development",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Generated
+3200
-3412
File diff suppressed because it is too large.
Load diff
+54
-36
@@ -2,70 +2,84 @@
|
||||
"name": "netbird-dashboard",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"copy": "copyfiles -f ./node_modules/@axa-fr/react-oidc/dist/OidcServiceWorker.js ./public",
|
||||
"copytrusted": "copyfiles -f ./public/local/OidcTrustedDomains.js ./public",
|
||||
"dev": "next dev -p 3000",
|
||||
"turbo": "next dev -p 3000 --turbo",
|
||||
"build": "next build",
|
||||
"postbuild": "node postbuild.js",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"cypress:open": "cypress open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@axa-fr/react-oidc": "^7.22.18",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-hover-card": "^1.1.4",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-radio-group": "^1.1.3",
|
||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slider": "^1.1.2",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tabler/icons-react": "^2.39.0",
|
||||
"@axa-fr/react-oidc": "^7.26.3",
|
||||
"@dagrejs/dagre": "^1.1.5",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tabler/icons-react": "^3.36.1",
|
||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||
"@tanstack/react-table": "^8.10.7",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/lodash": "^4.14.200",
|
||||
"@types/node": "20.10.6",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"@xyflow/react": "^12.8.4",
|
||||
"autoprefixer": "^10",
|
||||
"chart.js": "^4.4.8",
|
||||
"chroma-js": "^3.1.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"classnames": "^2.5.1",
|
||||
"clsx": "^2.0.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"cmdk": "^1.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"d3": "^7.9.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"eslint": "^8",
|
||||
"elkjs": "^0.10.0",
|
||||
"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",
|
||||
"framer-motion": "^12.29.2",
|
||||
"ip-address": "^10.1.0",
|
||||
"ip-cidr": "^3.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.460.0",
|
||||
"next": "^14.2.28",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lodash": "^4.17.23",
|
||||
"lucide-react": "^0.539.0",
|
||||
"next": "^16.1.6",
|
||||
"next-themes": "^0.2.1",
|
||||
"punycode": "^2.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.9.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react": "^19.2.4",
|
||||
"react-day-picker": "^9.13.0",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-ga4": "^2.1.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-hotjar": "^6.2.0",
|
||||
"react-hotjar": "^6.3.1",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-jwt": "^1.2.0",
|
||||
"react-loading-skeleton": "^3.3.1",
|
||||
"react-responsive": "^9.0.2",
|
||||
@@ -73,13 +87,17 @@
|
||||
"swr": "^2.2.4",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"timescape": "^0.7.1",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^13.13.0",
|
||||
"eslint-config-next": "^14.2.28",
|
||||
"@faker-js/faker": "^9.5.1",
|
||||
"@types/chroma-js": "^3.1.1",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-next": "^16.1.6",
|
||||
"postcss": "^8",
|
||||
"prettier": "3.0.3",
|
||||
"tailwindcss": "^3"
|
||||
"tailwindcss": "^3.4.17"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
const { resolve, join } = require("path");
|
||||
const { createHash } = require("crypto");
|
||||
const {
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
statSync,
|
||||
} = require("fs");
|
||||
|
||||
process.env.NODE_ENV = "production";
|
||||
const PLACEHOLDER = "NB_INLINE_SCRIPT_PLACEHOLDER";
|
||||
console.log("Starting post-build script to extract inline scripts...");
|
||||
|
||||
// Function to find HTML files recursively
|
||||
function findHtmlFiles(dir) {
|
||||
const files = [];
|
||||
const entries = readdirSync(dir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry);
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
files.push(...findHtmlFiles(fullPath));
|
||||
} else if (entry.endsWith(".html")) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
// For Next.js export output, the files are in the 'out' directory
|
||||
const baseDir = resolve("out");
|
||||
const htmlFiles = findHtmlFiles(baseDir);
|
||||
|
||||
console.log(`Found ${htmlFiles.length} .html files to process`);
|
||||
|
||||
// Ensure assets directory exists
|
||||
const assetsDir = `${baseDir}/assets`;
|
||||
mkdirSync(assetsDir, { recursive: true });
|
||||
|
||||
htmlFiles.forEach((file) => {
|
||||
// Read file contents
|
||||
const contents = readFileSync(file, "utf8");
|
||||
const scripts = [];
|
||||
|
||||
// Extract inline scripts
|
||||
const newFile = contents.replace(
|
||||
/<script(?![^>]*src)([^>]*)>(.+?)<\/script>/gs,
|
||||
(match, attributes, scriptContent) => {
|
||||
// Skip if script has src attribute (external script)
|
||||
if (attributes.includes("src=")) {
|
||||
return match;
|
||||
}
|
||||
|
||||
const addPlaceholderString = scripts.length === 0;
|
||||
const cleanedScript = scriptContent.trim();
|
||||
|
||||
if (cleanedScript) {
|
||||
scripts.push(
|
||||
`${cleanedScript}${cleanedScript.endsWith(";") ? "" : ";"}`,
|
||||
);
|
||||
}
|
||||
|
||||
return addPlaceholderString ? PLACEHOLDER : "";
|
||||
},
|
||||
);
|
||||
|
||||
// Early exit if no inline scripts found
|
||||
if (!scripts.length) {
|
||||
console.log(`No inline scripts found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine scripts and create hash
|
||||
const chunk = scripts.join("\n");
|
||||
const hash = createHash("md5").update(chunk).digest("hex").slice(0, 8);
|
||||
const chunkFileName = `chunk.${hash}.js`;
|
||||
const chunkPath = `${assetsDir}/${chunkFileName}`;
|
||||
|
||||
// Write the chunk file
|
||||
writeFileSync(chunkPath, chunk, "utf8");
|
||||
|
||||
// Replace placeholder string with script tag
|
||||
const updatedFile = newFile.replace(
|
||||
PLACEHOLDER,
|
||||
`<script src="/assets/${chunkFileName}" crossorigin=""></script>`,
|
||||
);
|
||||
|
||||
// Write updated HTML file
|
||||
writeFileSync(file, updatedFile, "utf8");
|
||||
});
|
||||
|
||||
console.log("Post-build script completed successfully!");
|
||||
File diff suppressed because it is too large.
Load diff
File renamed without changes.
@@ -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"} />;
|
||||
}
|
||||
@@ -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 } =
|
||||
@@ -30,7 +33,7 @@ export default function AccessControlPage() {
|
||||
<div className={"p-default py-6"}>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item
|
||||
href={"/policies"}
|
||||
href={"/access-control"}
|
||||
label={"Access Control"}
|
||||
icon={<AccessControlIcon size={14} />}
|
||||
/>
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { globalMetaTitle } from "@utils/meta";
|
||||
import type { Metadata } from "next";
|
||||
import BlankLayout from "@/layouts/BlankLayout";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `Control Center - ${globalMetaTitle}`,
|
||||
};
|
||||
export default BlankLayout;
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 20 of 449 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user