Compare commits

..
3 Commits
8 changed files with 151 additions and 42 deletions

No files matched your search

+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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"
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+28
View File
@@ -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
View File
@@ -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">