mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ced3088a | ||
|
|
42b7a15466 | ||
|
|
c88bfa6476 |
No files matched your search
@@ -58,11 +58,12 @@ 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_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 \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE"
|
||||
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 \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
|
||||
|
||||
MAIN_JS=$(find /usr/share/nginx/html/static/js/main.*js)
|
||||
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
|
||||
|
||||
+1
-12
@@ -70,16 +70,7 @@ function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
borderRadius: 4,
|
||||
colorPrimary: "#1890ff",
|
||||
fontFamily: "Arial"
|
||||
},
|
||||
components: {Badge: {fontSizeSM: 20}},
|
||||
}}
|
||||
>
|
||||
|
||||
<Provider store={store}>
|
||||
{!show && <SecureLoading padding="3em" width={50} height={50}/>}
|
||||
{show &&
|
||||
@@ -124,8 +115,6 @@ function App() {
|
||||
</Layout>
|
||||
}
|
||||
</Provider>
|
||||
|
||||
</ConfigProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+2
-1
@@ -11,5 +11,6 @@
|
||||
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
|
||||
"redirectURI": "$AUTH_REDIRECT_URI",
|
||||
"silentRedirectURI": "$AUTH_SILENT_REDIRECT_URI",
|
||||
"tokenSource": "$NETBIRD_TOKEN_SOURCE"
|
||||
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
|
||||
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS"
|
||||
}
|
||||
@@ -39,5 +39,7 @@ export function getConfig() {
|
||||
redirectURI: redirectURI,
|
||||
silentRedirectURI: silentRedirectURI,
|
||||
tokenSource: tokenSource,
|
||||
// drags all the query params to the auth layer specified in the URL when accessing dashboard.
|
||||
dragQueryParams: configJson.dragQueryParams == "true"
|
||||
};
|
||||
}
|
||||
+14
-2
@@ -474,6 +474,18 @@ ul.ant-list-items {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.route-form.edit-form-wrapper .ant-form-item{
|
||||
margin-bottom: 39px;
|
||||
.route-form.edit-form-wrapper .ant-form-item {
|
||||
margin-bottom: 39px;
|
||||
}
|
||||
|
||||
.groupsSelect {
|
||||
max-width: 200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1220px) {
|
||||
.groupsSelect{
|
||||
margin-left: 0;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
+44
-4
@@ -10,6 +10,8 @@ import {BrowserRouter} from "react-router-dom";
|
||||
import Loading from "./components/Loading";
|
||||
import LoginError from "./components/LoginError";
|
||||
import {AuthorityConfiguration} from "@axa-fr/react-oidc/dist/vanilla/oidc";
|
||||
import InstallPage from "./views/Install";
|
||||
import {ConfigProvider} from "antd";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
@@ -17,12 +19,29 @@ const config = getConfig();
|
||||
// is required for doing logout. Therefore, we need to hardcode the config for auth
|
||||
const auth0AuthorityConfig: AuthorityConfiguration = {
|
||||
authorization_endpoint: new URL("authorize", config.authority).href,
|
||||
token_endpoint: new URL("oauth/token", config.authority).href,
|
||||
token_endpoint: new URL("oauth/token", config.authority).href,
|
||||
revocation_endpoint: new URL("oauth/revoke", config.authority).href,
|
||||
end_session_endpoint: new URL("v2/logout", config.authority).href,
|
||||
userinfo_endpoint: new URL("userinfo", config.authority).href,
|
||||
} as AuthorityConfiguration
|
||||
|
||||
const buildExtras = (config: any) => {
|
||||
type Extras = { [key: string]: string }
|
||||
let extras: Extras = {};
|
||||
|
||||
if (config.dragQueryParams) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.forEach((value, key) => {
|
||||
extras[key] = value
|
||||
});
|
||||
}
|
||||
|
||||
if (config.audience) {
|
||||
extras.audience = config.audience
|
||||
}
|
||||
return extras
|
||||
}
|
||||
|
||||
const providerConfig = {
|
||||
authority: config.authority,
|
||||
client_id: config.clientId,
|
||||
@@ -34,7 +53,7 @@ const providerConfig = {
|
||||
// service_worker_relative_url:'/OidcServiceWorker.js',
|
||||
service_worker_only: false,
|
||||
authority_configuration: config.auth0Auth ? auth0AuthorityConfig : undefined,
|
||||
...(config.audience ? {extras: {audience: config.audience}} : null),
|
||||
extras: buildExtras(config),
|
||||
...(config.clientSecret ? {token_request_extras: {client_secret: config.clientSecret}} : null)
|
||||
};
|
||||
|
||||
@@ -44,8 +63,14 @@ const root = ReactDOM.createRoot(
|
||||
|
||||
const loadingComponent = () => <Loading padding="3em" width={50} height={50}/>
|
||||
|
||||
root.render(
|
||||
<OidcProvider
|
||||
const showApp = () => {
|
||||
if (window.location.pathname === "/install") {
|
||||
// We bypass authentication for pages that do not require auth.
|
||||
// E.g., when we just want to show installation steps for public.
|
||||
return <InstallPage/>
|
||||
}
|
||||
|
||||
return <OidcProvider
|
||||
configuration={providerConfig}
|
||||
callbackSuccessComponent={loadingComponent}
|
||||
authenticatingErrorComponent={LoginError}
|
||||
@@ -60,6 +85,21 @@ root.render(
|
||||
<App/>
|
||||
</BrowserRouter>
|
||||
</OidcProvider>
|
||||
}
|
||||
|
||||
root.render(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
borderRadius: 4,
|
||||
colorPrimary: "#1890ff",
|
||||
fontFamily: "Arial"
|
||||
},
|
||||
components: {Badge: {fontSizeSM: 20}},
|
||||
}}
|
||||
>
|
||||
{showApp()}
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import {Container} from "../components/Container";
|
||||
import { Modal,
|
||||
} from "antd";
|
||||
import AddPeerPopup from "../components/popups/addpeer/addpeer/AddPeerPopup";
|
||||
|
||||
|
||||
export const InstallPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Container style={{paddingTop: "40px"}}>
|
||||
<Modal
|
||||
closable={false}
|
||||
open={true}
|
||||
footer={[]}
|
||||
width={780}
|
||||
>
|
||||
<AddPeerPopup
|
||||
greeting={"Hi there!"}
|
||||
headline={ "It's time to add your first device."}
|
||||
/>
|
||||
</Modal>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstallPage;
|
||||
+58
-22
@@ -49,13 +49,17 @@ import { usePageSizeHelpers } from "../utils/pageSize";
|
||||
import AddPeerPopup from "../components/popups/addpeer/addpeer/AddPeerPopup";
|
||||
import { getLocalItem, setLocalItem, StorageKey } from "../services/local";
|
||||
import { useOidcUser } from "@axa-fr/react-oidc";
|
||||
import { useGetGroupTagHelpers } from "../utils/groups";
|
||||
|
||||
const { Title, Paragraph, Text } = Typography;
|
||||
const { Column } = Table;
|
||||
const { confirm } = Modal;
|
||||
const { Option } = Select;
|
||||
|
||||
export const Peers = () => {
|
||||
const { onChangePageSize, pageSizeOptions, pageSize } = usePageSizeHelpers();
|
||||
const { optionRender, blueTagRender, tagGroups, dropDownRender } =
|
||||
useGetGroupTagHelpers();
|
||||
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
@@ -79,6 +83,7 @@ export const Peers = () => {
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
|
||||
const [textToSearch, setTextToSearch] = useState("");
|
||||
const [groupsFilterArray, setGroupsFilterArray] = useState([]);
|
||||
const [optionOnOff, setOptionOnOff] = useState("all");
|
||||
const [dataTable, setDataTable] = useState([] as PeerDataTable[]);
|
||||
const [peerToAction, setPeerToAction] = useState(
|
||||
@@ -124,7 +129,7 @@ export const Peers = () => {
|
||||
}, [users]);
|
||||
|
||||
useEffect(() => {
|
||||
if ((!loading && peers && groups)) {
|
||||
if (!loading && peers && groups) {
|
||||
const quickFilter = getFilterState("peerFilter", "quickFilter");
|
||||
if (quickFilter) setOptionOnOff(quickFilter);
|
||||
|
||||
@@ -134,8 +139,13 @@ export const Peers = () => {
|
||||
const pageSize = getFilterState("peerFilter", "pageSize");
|
||||
if (pageSize) onChangePageSize(pageSize, "peerFilter");
|
||||
|
||||
if (quickFilter || searchText || pageSize) {
|
||||
setDataTable(transformDataTable(filterDataTable(searchText)));
|
||||
const groupFilter = getFilterState("peerFilter", "groupFilter") || [];
|
||||
if (groupFilter) setGroupsFilterArray(groupFilter);
|
||||
|
||||
if (quickFilter || searchText || pageSize || groupFilter) {
|
||||
setDataTable(
|
||||
transformDataTable(filterDataTable(searchText, groupFilter))
|
||||
);
|
||||
} else {
|
||||
setDataTable(transformDataTable(peers));
|
||||
}
|
||||
@@ -198,7 +208,7 @@ export const Peers = () => {
|
||||
}, [peers, groups]);
|
||||
|
||||
useEffect(() => {
|
||||
setDataTable(transformDataTable(filterDataTable("")));
|
||||
setDataTable(transformDataTable(filterDataTable("", groupsFilterArray)));
|
||||
}, [textToSearch, optionOnOff]);
|
||||
|
||||
const deleteKey = "deleting";
|
||||
@@ -226,10 +236,16 @@ export const Peers = () => {
|
||||
}
|
||||
}, [deletedPeer]);
|
||||
|
||||
const filterDataTable = (searchText: string): Peer[] => {
|
||||
const t = searchText
|
||||
? searchText.toLowerCase().trim()
|
||||
: textToSearch.toLowerCase().trim();
|
||||
const handleGroupChange = (value: any) => {
|
||||
setGroupsFilterArray(value);
|
||||
storeFilterState("peerFilter", "groupFilter", value);
|
||||
setDataTable(transformDataTable(filterDataTable(textToSearch, value)));
|
||||
};
|
||||
|
||||
const filterDataTable = (searchText: string, groupIDs: string[]): Peer[] => {
|
||||
const t = searchText
|
||||
? searchText.toLowerCase().trim()
|
||||
: textToSearch.toLowerCase().trim();
|
||||
let f: Peer[] = filter(peers, (f: Peer) => {
|
||||
let userEmail: string | null;
|
||||
const u = users?.find((u) => u.id === f.user_id)?.email;
|
||||
@@ -246,21 +262,24 @@ export const Peers = () => {
|
||||
if (optionOnOff === "on") {
|
||||
f = filter(peers, (f: Peer) => f.connected);
|
||||
}
|
||||
return f;
|
||||
};
|
||||
|
||||
const getGroupNamesFromIDs = (
|
||||
groupIDList: string[] | undefined
|
||||
): string[] => {
|
||||
if (!groupIDList) {
|
||||
return [];
|
||||
let filterByGroupArray: any = [];
|
||||
let tempFilterByGroupArray: any = [];
|
||||
if (groupIDs.length) {
|
||||
f.forEach((element) => {
|
||||
element.groups?.forEach((element2) => {
|
||||
if (element2.id && groupIDs.includes(element2.id)) {
|
||||
if (!tempFilterByGroupArray.includes(element.id)) {
|
||||
filterByGroupArray.push(element);
|
||||
tempFilterByGroupArray.push(element.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
f = filterByGroupArray;
|
||||
}
|
||||
|
||||
return (
|
||||
groups
|
||||
?.filter((g) => groupIDList.includes(g.id!))
|
||||
.map((g) => g.name || "") || []
|
||||
);
|
||||
return f;
|
||||
};
|
||||
|
||||
const onChangeTextToSearch = (
|
||||
@@ -626,7 +645,7 @@ export const Peers = () => {
|
||||
allowClear
|
||||
value={textToSearch}
|
||||
// onPressEnter={searchDataTable}
|
||||
placeholder="Search by name, IP or owner..."
|
||||
placeholder="Search by name, IP, owner, or group..."
|
||||
onChange={onChangeTextToSearch}
|
||||
/>
|
||||
</Col>
|
||||
@@ -639,7 +658,7 @@ export const Peers = () => {
|
||||
xxl={11}
|
||||
span={11}
|
||||
>
|
||||
<Space size="middle">
|
||||
<Space size="middle" style={{ marginRight: "15px" }}>
|
||||
<Radio.Group
|
||||
options={optionsOnOff}
|
||||
onChange={onChangeOnOff}
|
||||
@@ -658,6 +677,23 @@ export const Peers = () => {
|
||||
className="select-rows-per-page-en"
|
||||
/>
|
||||
</Space>
|
||||
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="Filter by groups"
|
||||
tagRender={blueTagRender}
|
||||
// dropdownRender={dropDownRender}
|
||||
optionFilterProp="serchValue"
|
||||
className="groupsSelect"
|
||||
onChange={handleGroupChange}
|
||||
value={groupsFilterArray}
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Row justify="end">
|
||||
|
||||
Reference in new issue
Block a user