Compare commits

...
30 Commits
Author SHA1 Message Date
Misha Bragin 7a29dac01c Add single line installer command for Mac and Linux (#162) 2023-04-04 14:20:02 +02:00
pascal-fischer 444e9ec44a fetch userID from user list and not oidc by default (#161) 2023-04-03 16:52:42 +02:00
pascal-fischer dff0313f82 Feature/pat support (#157)
* Add working UI + API calls [missing token popup]

* use popup view to add new token

* show userID if user name not available

* switch from description to name

* show "Me" instead of own name

* removed created_by column

* update add token explanation

* use object instead of plain text for token create response

* some style changes

* disable information button for tokens

* last_used can contain nil

* fix delete popups

* lower case letters for dates

* add activity and fix visibility

* show settings tab for non admins

* remove spaces on top of setting tabs

* fix copy button size and position

* fix list footers

* continue merge changes to new files
2023-04-03 12:29:40 +02:00
Maycon Santos 11fbfb336a Clean last estimated name (#160) 2023-04-03 12:16:12 +02:00
Maycon Santos 4a0ae8f27d Add missing config change (#159) 2023-04-01 21:58:46 +02:00
Maycon Santos 9a72d8b0c4 Allow defining API token source (#158)
On many IDP providers, the access token
 is used to access the IDP's own API

 With these changes, we allow users to define the proper token to be used for
 management API calls
2023-04-01 19:44:28 +02:00
Misha Bragin 8e038cf242 Make table headers font-weight normal (#156) 2023-03-27 15:59:18 +02:00
Misha Bragin 5bd94eff56 Fix add peer popup tab layout on mobile (#154) 2023-03-23 12:10:27 +01:00
Misha Bragin 77f065b093 Update theme border radius (#152) 2023-03-22 16:56:36 +01:00
Misha Bragin a4d55cfb90 Fix settings modal styles (#151) 2023-03-22 16:14:49 +01:00
Misha Bragin cfd4c9075b Add onboarding steps (#150) 2023-03-22 15:21:52 +01:00
braginini 962180030a Fix activity docs link 2023-03-15 14:20:48 +01:00
Misha Bragin 485e1e8d79 Add more references to docs (#149) 2023-03-15 14:18:22 +01:00
Givi Khojanashvili b11007b29f Add policy add activities (#147)
Related changes for netbirdio/netbird#700
2023-03-13 10:58:34 +01:00
Maycon Santos bce75c1ca9 Disable banner (#146) 2023-03-02 15:55:31 +01:00
Maycon Santos 0c09992b38 Use user.is_current (#145)
prevent update for own user

use the is_current for label

removed unused imports
2023-03-01 22:50:26 +01:00
Maycon Santos 86b12f30d2 Use new windows release URL (#144)
removed latestVersion logic and environment support
2023-03-01 15:00:36 +01:00
Maycon Santos d3e34d8448 Use single page size helper with new larger values (#143)
Reduce duplicated code with single helper for pagination

On larger deployments we should allow for larger pages

fix popover key issue and vertical spacing

update deprecated keys
2023-02-28 16:32:12 +01:00
Misha Bragin 76083168f6 Log error when activity event is not handled (#142) 2023-02-28 11:22:40 +01:00
Misha Bragin a54b3687ae Display user in the user role update event (#141)
Handling of the "User role updated" event (user.role.update) 
was missing in the Activity tab.
2023-02-27 15:27:00 +01:00
Zoltan Papp 25f154dc83 Capitalize first letter of OS (#140) 2023-02-27 14:54:39 +01:00
pascal-fischer f3c7d877f8 Add additional step in the Mac installation instructions to start the daemon (#138) 2023-02-22 20:55:36 +01:00
Misha Bragin 7cea7e7f54 Format individual peer login expiration event (#139)
Events peer.login.expiration.disable and peer.login.expiration.enable
are handled in the Activity tab now and properly displayed.
2023-02-22 20:54:56 +01:00
Misha Bragin aaa351635f Add peer expiration setting confirmation modal (#137)
Add a confirmation dialog to notify a user of possible
consequences of the peer login expiration enabling/disabling.
2023-02-21 08:47:14 +01:00
Misha Bragin 379ff5486e Account settings view (#136)
New Settings tab added to the dashboard.
It is possible to enable or disable peer login expiration globally for an account.
As well as defining the expiration time period.
2023-02-20 08:57:24 +01:00
Misha Bragin 8bcd9918e2 Fix expires in input filed of the setup key (#135) 2023-02-17 14:30:22 +01:00
Misha Bragin 044ccd0ce6 Display login expiration activities (#134) 2023-02-16 15:36:45 +01:00
braginini ab09ca3697 Display API error in peers view 2023-02-16 13:03:39 +01:00
Misha Bragin 1644ed5dce Add peer login expiration tooltip (#133) 2023-02-16 12:47:35 +01:00
Misha Bragin cea459792f Add peer login expiration (#132)
Display if peer login has expired in the Peers table
Enable/Disable peer login expiration
2023-02-16 12:18:15 +01:00
61 changed files with 2936 additions and 973 deletions

No files matched your search

+2 -4
View File
@@ -52,14 +52,12 @@ export AUTH_SUPPORTED_SCOPES=${AUTH_SUPPORTED_SCOPES:-openid profile email api o
export NETBIRD_MGMT_API_ENDPOINT=$(echo $NETBIRD_MGMT_API_ENDPOINT | sed -E 's/(:80|:443)$//')
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}
REPO="https://github.com/netbirdio/netbird/"
# this command will fetch the latest release e.g. v0.6.3
export NETBIRD_LATEST_VERSION=$(basename $(curl -fs -o/dev/null -w %{redirect_url} ${REPO}releases/latest))
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
# replace ENVs in the config
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_LATEST_VERSION \$\$NETBIRD_HOTJAR_TRACK_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI"
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$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"
MAIN_JS=$(find /usr/share/nginx/html/static/js/main.*js)
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
+740 -437
View File
File diff suppressed because it is too large. Load diff
+4 -2
View File
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@ant-design/icons": "^4.8.0",
"@axa-fr/react-oidc": "^5.14.0",
"@headlessui/react": "^1.5.0",
"@heroicons/react": "^1.0.4",
@@ -18,7 +18,7 @@
"@types/react-redux": "^7.1.24",
"@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.25",
"antd": "^4.20.6",
"antd": "^5.3.1",
"autoprefixer": "^10.4.4",
"axios": "^0.27.2",
"cidr-regex": "^3.1.1",
@@ -27,11 +27,13 @@
"highlight.js": "^11.2.0",
"history": "^5.0.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"postcss": "^8.4.12",
"prop-types": "^15.7.2",
"punycode": "^2.1.1",
"rc-overflow": "^1.2.8",
"react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.1.0",
"react-hotjar": "^5.1.0",
"react-redux": "^8.0.2",
+59 -47
View File
@@ -4,7 +4,7 @@ import {apiClient, store} from "./store";
import {hotjar} from 'react-hotjar';
import {getConfig} from "./config";
import Banner from "./components/Banner";
import {Col, Layout, Row} from "antd";
import {Col, ConfigProvider, Layout, Row} from "antd";
import {Container} from "./components/Container";
import Navbar from "./components/Navbar";
import {Redirect, Route, Switch} from "react-router-dom";
@@ -16,12 +16,12 @@ import SetupKeys from "./views/SetupKeys";
import AccessControl from "./views/AccessControl";
import Users from "./views/Users";
import FooterComponent from "./components/FooterComponent";
import {useGetAccessTokenSilently} from "./utils/token";
import {useGetTokenSilently, useTokenSource} from "./utils/token";
import {User} from "./store/user/types";
import {SecureLoading} from "./components/Loading";
import DNS from "./views/DNS";
import Activity from "./views/Activity";
import Settings from "./views/Settings";
const {Header, Content} = Layout;
@@ -29,8 +29,9 @@ const {Header, Content} = Layout;
function App() {
const run = useRef(false)
const [show, setShow] = useState(false)
const {getAccessTokenSilently} = useGetAccessTokenSilently();
const {hotjarTrackID} = getConfig();
const {hotjarTrackID,tokenSource} = getConfig();
useTokenSource(tokenSource)
const {getTokenSilently} = useGetTokenSilently();
// @ts-ignore
if (hotjarTrackID && window._DATADOG_SYNTHETICS_BROWSER === undefined) {
hotjar.initialize(hotjarTrackID, 6);
@@ -55,9 +56,9 @@ function App() {
useEffect(() => {
if (!run.current) {
run.current = true
apiClient.request<User[]>('GET', `/api/users`, {getAccessTokenSilently: getAccessTokenSilently})
apiClient.request<User[]>('GET', `/api/users`, {getAccessTokenSilently: getTokenSilently})
.then(() => {
setShow(true)
setShow(true)
})
.catch(e => {
setShow(true)
@@ -65,54 +66,65 @@ function App() {
})
}
}, [getAccessTokenSilently])
}, [getTokenSilently])
return (
<>
<ConfigProvider
theme={{
token: {
borderRadius: 4,
colorPrimary: "#1890ff",
fontFamily: "Arial"
},
}}
>
<Provider store={store}>
{!show && <SecureLoading padding="3em" width={50} height={50}/>}
{show &&
<Layout>
<Banner/>
<Header className="header" style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
alignContent: "center"
}}>
<Row justify="space-around" align="middle">
<Col span={24}>
<Container>
<Navbar/>
</Container>
</Col>
</Row>
</Header>
<Content style={{minHeight: "100vh"}}>
<Switch>
<Route
exact
path="/"
render={() => {
return (
<Redirect to="/peers"/>
)
}}
/>
<Route path='/peers' exact component={withOidcSecure(Peers)}/>
<Route path="/add-peer" component={withOidcSecure(AddPeer)}/>
<Route path="/setup-keys" component={withOidcSecure(SetupKeys)}/>
<Route path="/acls" component={withOidcSecure(AccessControl)}/>
<Route path="/routes" component={withOidcSecure(Routes)}/>
<Route path="/users" component={withOidcSecure(Users)}/>
<Route path="/dns" component={withOidcSecure(DNS)}/>
<Route path="/activity" component={withOidcSecure(Activity)}/>
</Switch>
</Content>
<FooterComponent/>
</Layout>
<Layout>
<Banner/>
<Header className="header" style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
alignContent: "center"
}}>
<Row justify="space-around" align="middle">
<Col span={24}>
<Container>
<Navbar/>
</Container>
</Col>
</Row>
</Header>
<Content style={{minHeight: "100vh"}}>
<Switch>
<Route
exact
path="/"
render={() => {
return (
<Redirect to="/peers"/>
)
}}
/>
<Route path='/peers' exact component={withOidcSecure(Peers)}/>
<Route path="/setup-keys" component={withOidcSecure(SetupKeys)}/>
<Route path="/acls" component={withOidcSecure(AccessControl)}/>
<Route path="/routes" component={withOidcSecure(Routes)}/>
<Route path="/users" component={withOidcSecure(Users)}/>
<Route path="/dns" component={withOidcSecure(DNS)}/>
<Route path="/activity" component={withOidcSecure(Activity)}/>
<Route path="/settings" component={withOidcSecure(Settings)}/>
</Switch>
</Content>
<FooterComponent/>
</Layout>
}
</Provider>
</ConfigProvider>
</>
)
}
+1 -1
View File
@@ -16,7 +16,7 @@ const AccessControlModalGroups:React.FC<Props> = ({data, title, visible, onCance
return (
<>
<Modal title={title} visible={isModalVisible} onCancel={() => onCancel()} footer={null}>
<Modal title={title} open={isModalVisible} onCancel={() => onCancel()} footer={null}>
<List
itemLayout="horizontal"
dataSource={data as Group[] | undefined}
+4 -6
View File
@@ -23,7 +23,7 @@ import {Rule, RuleToSave} from "../store/rule/types";
import {uniq} from "lodash"
import {Header} from "antd/es/layout/layout";
import {RuleObject} from "antd/lib/form";
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
const {Paragraph} = Typography;
const {Option} = Select;
@@ -34,7 +34,7 @@ interface FormRule extends Rule {
}
const AccessControlNew = () => {
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const setupNewRuleVisible = useSelector((state: RootState) => state.rule.setupNewRuleVisible)
const groups = useSelector((state: RootState) => state.group.data)
@@ -103,7 +103,7 @@ const AccessControlNew = () => {
.then((values) => {
const ruleToSave = createRuleToSave()
dispatch(ruleActions.saveRule.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: ruleToSave
}))
})
@@ -408,9 +408,7 @@ const AccessControlNew = () => {
<Col span={24}>
<Divider></Divider>
<Button icon={<QuestionCircleFilled/>} type="link" target="_blank"
href="https://docs.netbird.io/docs/overview/acls"
style={{color: 'rgb(07, 114, 128)'}}>Learn
more about access controls</Button>
href="https://docs.netbird.io/docs/overview/acls">Learn more about access controls</Button>
</Col>
</Row>
</Form>
+1 -1
View File
@@ -36,7 +36,7 @@ const Banner = () => {
if((!stored_banner_closed || stored_banner_closed !== 'true') ||
(!store_banner_md5 || store_banner_md5 !== announcement_md5)) {
setShow(true);
//setShow(true);
localStorage.setItem(banner_md5_key,announcement_md5);
localStorage.setItem(banner_closed_key,'false');
}
+4 -6
View File
@@ -32,7 +32,7 @@ import {RuleObject} from "antd/lib/form";
import cidrRegex from 'cidr-regex';
import {NameServer, NameServerGroup, NameServerGroupToSave} from "../store/nameservers/types";
import {useGetGroupTagHelpers} from "../utils/groups"
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
const {Paragraph} = Typography;
@@ -51,7 +51,7 @@ const NameServerGroupUpdate = () => {
selectValidator
} = useGetGroupTagHelpers()
const dispatch = useDispatch()
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const {Option} = Select;
const nsGroup = useSelector((state: RootState) => state.nameserverGroup.nameserverGroup)
const setupNewNameServerGroupVisible = useSelector((state: RootState) => state.nameserverGroup.setupNewNameServerGroupVisible)
@@ -220,7 +220,7 @@ const NameServerGroupUpdate = () => {
.then((values) => {
const nsGroupToSave = createNSGroupToSave(values as NameServerGroup)
dispatch(nsGroupActions.saveNameServerGroup.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: nsGroupToSave
}))
@@ -604,9 +604,7 @@ const NameServerGroupUpdate = () => {
<Col span={24}>
<Divider></Divider>
<Button icon={<QuestionCircleFilled/>} type="link" target="_blank"
href="https://netbird.io/docs/how-to-guides/nameservers"
style={{color: 'rgb(07, 114, 128)'}}>Learn
more about nameservers</Button>
href="https://netbird.io/docs/how-to-guides/nameservers">Learn more about nameservers</Button>
</Col>
</Row>
</Form>) :
+5 -5
View File
@@ -11,7 +11,7 @@ import {User} from "../store/user/types";
import {useDispatch, useSelector} from "react-redux";
import {RootState} from "typesafe-actions";
import {actions as userActions} from "../store/user";
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
const {useBreakpoint} = Grid;
@@ -19,7 +19,7 @@ const Navbar = () => {
let location = useLocation();
const config = getConfig();
const { logout } = useOidc();
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const {oidcUser} = useOidcUser();
@@ -35,13 +35,13 @@ const Navbar = () => {
const items = [
{label: (<Link to="/peers">Peers</Link>), key: '/peers'},
{label: (<Link to="/add-peer">Add Peer</Link>), key: '/add-peer'},
{label: (<Link to="/setup-keys">Setup Keys</Link>), key: '/setup-keys'},
{label: (<Link to="/acls">Access Control</Link>), key: '/acls'},
{label: (<Link to="/routes">Network Routes</Link>), key: '/routes'},
{ label: (<Link to="/dns">DNS</Link>), key: '/dns' },
{label: (<Link to="/users">Users</Link>), key: '/users'},
{label: (<Link to="/activity">Activity</Link>), key: '/activity'}
{label: (<Link to="/activity">Activity</Link>), key: '/activity'},
{label: (<Link to="/settings">Settings</Link>), key: '/settings'}
] as ItemType[]
const userEmailKey = 'user-email'
@@ -80,7 +80,7 @@ const Navbar = () => {
window.location.pathname !== '/peers' &&
window.location.pathname !== '/users') {
setIsRefreshingUserState(true)
dispatch(userActions.getUsers.request({getAccessTokenSilently: getAccessTokenSilently, payload: null}))
dispatch(userActions.getUsers.request({getAccessTokenSilently: getTokenSilently, payload: null}))
return
}
if (users.length === 0 && isRefreshingUserState) {
+30 -26
View File
@@ -2,14 +2,14 @@ import React, {useEffect, useRef, useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
import {RootState} from "typesafe-actions";
import {actions as peerActions} from '../store/peer';
import {Button, Col, Collapse, Divider, Drawer, Form, Input, Row, Select, Space, Tag, Typography} from "antd";
import {Button, Col, Collapse, Divider, Drawer, Form, Input, Radio, Row, Select, Space, Tag, Typography} from "antd";
import {Header} from "antd/es/layout/layout";
import type {CustomTagProps} from 'rc-select/lib/BaseSelect'
import {FormPeer, Peer, PeerGroupsToSave} from "../store/peer/types";
import {Group, GroupPeer} from "../store/group/types";
import {CloseOutlined, EditOutlined} from "@ant-design/icons";
import {RuleObject} from 'antd/lib/form';
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
import {timeAgo} from "../utils/common";
const {Paragraph} = Typography;
@@ -18,7 +18,8 @@ const {Panel} = Collapse;
const punycode = require('punycode/')
const PeerUpdate = () => {
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const groups = useSelector((state: RootState) => state.group.data)
const users = useSelector((state: RootState) => state.user.data)
@@ -190,6 +191,7 @@ const PeerUpdate = () => {
setCallingPeerAPI(false)
setCallingPeerAPI(false)
setSubmitRunning(false)
setEstimatedName("")
}
const noUpdateToGroups = (): Boolean => {
@@ -200,6 +202,10 @@ const PeerUpdate = () => {
return !formPeer.name || formPeer.name === peer.name
}
const noUpdateToLoginExpiration = (): Boolean => {
return formPeer.login_expiration_enabled === peer.login_expiration_enabled
}
const onChange = (data: any) => {
setFormPeer({...formPeer, ...data})
}
@@ -232,7 +238,8 @@ const PeerUpdate = () => {
return {
id: formPeer.id,
ssh_enabled: formPeer.ssh_enabled,
name: formPeer.name
name: formPeer.name,
login_expiration_enabled: formPeer.login_expiration_enabled
} as Peer
}
@@ -240,18 +247,18 @@ const PeerUpdate = () => {
form.validateFields()
.then((values) => {
setSubmitRunning(true)
if (!noUpdateToName()) {
if (!noUpdateToName() || !noUpdateToLoginExpiration()) {
const peerUpdate = createPeerToSave()
setCallingPeerAPI(true)
dispatch(peerActions.updatePeer.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: peerUpdate
}))
}
if (peerGroupsToSave.groupsToRemove.length || peerGroupsToSave.groupsToAdd.length || peerGroupsToSave.groupsNoId.length) {
if (!noUpdateToGroups()) {
setCallingGroupAPI(true)
dispatch(peerActions.saveGroups.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: peerGroupsToSave
}))
}
@@ -303,7 +310,7 @@ const PeerUpdate = () => {
<Space style={{display: 'flex', justifyContent: 'end'}}>
<Button onClick={onCancel} disabled={savedGroups.loading}>Cancel</Button>
<Button type="primary"
disabled={(savedGroups.loading || updatedPeers.loading) || (noUpdateToGroups() && noUpdateToName())}
disabled={(savedGroups.loading || updatedPeers.loading) || (noUpdateToGroups() && noUpdateToName() && noUpdateToLoginExpiration())}
onClick={handleFormSubmit}>Save</Button>
</Space>
}
@@ -435,6 +442,20 @@ const PeerUpdate = () => {
</Form.Item>
</Col>
)}
<Col span={24}>
<Form.Item
name="login_expiration_enabled"
label="Login expiration"
tooltip="When login expires, the user has to re-authenticate this peer. This only applies to peers added with the SSO login."
>
<Radio.Group
options={[{label: 'Enabled', value: true}, {label: 'Disabled', value: false}]}
optionType="button"
buttonStyle="solid"
disabled={!formPeer.user_id}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="groupsNames"
@@ -458,23 +479,6 @@ const PeerUpdate = () => {
</Select>
</Form.Item>
</Col>
{/*<Col span={24}>
<Row wrap={false} gutter={12}>
<Col flex="none">
<FlagFilled/>
</Col>
<Col flex="auto">
<Paragraph>
Every peer is part of the group All, thus you can't remove it.
</Paragraph>
</Col>
</Row>
</Col>*/}
{/*<Col span={24}>
<Divider orientation="left" plain style={{color: "#5a5c5a"}}>
System Info
</Divider>
</Col>*/}
<Col span={24}>
<Collapse onChange={onChange} bordered={false} ghost={true}
style={{color: "#5a5c5a"}}>
+5 -7
View File
@@ -30,7 +30,7 @@ import {
routePeerSeparator,
transformGroupedDataTable
} from '../utils/routes'
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
import {useGetGroupTagHelpers} from "../utils/groups";
const {Paragraph} = Typography;
@@ -50,7 +50,7 @@ const RouteUpdate = () => {
selectValidator
} = useGetGroupTagHelpers()
const {Option} = Select;
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const setupNewRouteVisible = useSelector((state: RootState) => state.route.setupNewRouteVisible)
const setupNewRouteHA = useSelector((state: RootState) => state.route.setupNewRouteHA)
@@ -163,7 +163,7 @@ const RouteUpdate = () => {
if (!setupNewRouteHA || formRoute.peer != '') {
const routeToSave = createRouteToSave(formRoute)
dispatch(routeActions.saveRoute.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: routeToSave
}))
} else {
@@ -180,7 +180,7 @@ const RouteUpdate = () => {
}
const routeToSave = createRouteToSave(updateRoute)
dispatch(routeActions.saveRoute.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: routeToSave
}))
})
@@ -453,9 +453,7 @@ const RouteUpdate = () => {
<Col span={24}>
<Divider></Divider>
<Button icon={<QuestionCircleFilled/>} type="link" target="_blank"
href="https://netbird.io/docs/how-to-guides/network-routes"
style={{color: 'rgb(07, 114, 128)'}}>Learn
more about network routes</Button>
href="https://netbird.io/docs/how-to-guides/network-routes">Learn more about network routes</Button>
</Col>
</Row>
</Form>
+24 -48
View File
@@ -11,7 +11,7 @@ import {
Form,
Input,
InputNumber,
List,
List, Modal,
Radio,
Row,
Select,
@@ -23,25 +23,27 @@ import {RootState} from "typesafe-actions";
import {CloseOutlined, EditOutlined, QuestionCircleFilled} from "@ant-design/icons";
import {FormSetupKey, SetupKey, SetupKeyToSave} from "../store/setup-key/types";
import {Header} from "antd/es/layout/layout";
import {formatDate, timeAgo} from "../utils/common";
import {checkExpiresIn, formatDate, timeAgo} from "../utils/common";
import {RuleObject} from "antd/lib/form";
import {CustomTagProps} from "rc-select/lib/BaseSelect";
import {Group} from "../store/group/types";
import {useGetAccessTokenSilently} from "../utils/token";
import ExpiresInInput, {ExpiresInValue} from "../views/ExpiresInInput";
import {useGetTokenSilently} from "../utils/token";
import ExpiresInInput, {expiresInToSeconds, ExpiresInValue} from "../views/ExpiresInInput";
import moment from "moment";
const {Option} = Select;
const {Text} = Typography;
const ExpiresInDefault: ExpiresInValue = {number: 30, interval: "Days"}
const ExpiresInDefault: ExpiresInValue = {number: 30, interval: "day"}
const customExpiresFormat: DatePickerProps['format'] = value => {
return formatDate(value)
}
const customLastUsedFormat: DatePickerProps['format'] = value => {
if (value.toString().startsWith("0001")) {
if (value.year() == 1) {
// 1st of Jan 0001
return "never"
}
let ago = timeAgo(value.toString())
@@ -52,7 +54,7 @@ const customLastUsedFormat: DatePickerProps['format'] = value => {
}
const SetupKeyNew = () => {
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible)
const setupKey = useSelector((state: RootState) => state.setupKey.setupKey)
@@ -93,7 +95,9 @@ const SetupKeyNew = () => {
const fSetupKey = {
...setupKey,
autoGroupNames: setupKey.auto_groups ? formKeyGroups : [],
expiresInFormatted: ExpiresInDefault
expiresInFormatted: ExpiresInDefault,
exp: moment(setupKey.expires),
last: moment(setupKey.last_used)
} as FormSetupKey
setFormSetupKey(fSetupKey)
form.setFieldsValue(fSetupKey)
@@ -117,38 +121,13 @@ const SetupKeyNew = () => {
usage_limit: formSetupKey.usage_limit
} as SetupKeyToSave
}
const expiresInToSeconds = (expiresIn: ExpiresInValue): number => {
if (!expiresIn.number || !expiresIn.interval) {
return 0
}
let multiplier = 0
switch (expiresIn.interval.toLowerCase()) {
case "day":
multiplier = 24 * 3600
break
case "week":
multiplier = 7 * 24 * 3600
break
case "month":
multiplier = 30 * 24 * 3600
break
case "year":
multiplier = 365 * 24 * 3600
break
default:
multiplier = 0
}
return expiresIn.number * multiplier
}
const handleFormSubmit = () => {
form.validateFields()
.then((values) => {
let setupKeyToSave = createSetupKeyToSave()
dispatch(setupKeyActions.saveSetupKey.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: setupKeyToSave
}))
})
@@ -290,13 +269,6 @@ const SetupKeyNew = () => {
return setupKey.auto_groups?.filter(g => !formGroupIds.includes(g)).length > 0
}
const checkExpiresIn = (_: any, value: { number: number }) => {
if (value.number > 0) {
return Promise.resolve();
}
return Promise.reject(new Error("Expiration must be greater than zero"));
};
return (
<>
{setupKey &&
@@ -384,7 +356,7 @@ const SetupKeyNew = () => {
{setupKey.id && formSetupKey.name &&
<Col span={12}>
<Form.Item
name="expires"
name="exp"
label="Expires"
tooltip="The expiration date of the key"
>
@@ -397,7 +369,7 @@ const SetupKeyNew = () => {
{setupKey.id && formSetupKey.name &&
<Col span={12}>
<Form.Item
name="last_used"
name="last"
label="Last Used"
tooltip="The last time the key was used"
>
@@ -447,7 +419,12 @@ const SetupKeyNew = () => {
<Col span={24}>
<Form.Item name="expiresInFormatted" label="Expires In"
rules={[{validator: checkExpiresIn}]}>
<ExpiresInInput/>
<ExpiresInInput options={
Array.of(
{key: "day", title: "Days"},
{key: "month", title: "Months"},
{key: "year", title: "Years"})
}/>
</Form.Item>
</Col>}
<Col span={12}>
@@ -455,7 +432,8 @@ const SetupKeyNew = () => {
label="Usage Limit"
tooltip="Limit the number of times this key can be used. Use 0 for unlimited use."
>
<InputNumber min={0} defaultValue={0} disabled={setupKey.id || formSetupKey.type !== "reusable"}
<InputNumber min={0} defaultValue={0}
disabled={setupKey.id || formSetupKey.type !== "reusable"}
style={{width: "100%"}}
/>
</Form.Item>
@@ -496,9 +474,7 @@ const SetupKeyNew = () => {
<Col span={24}>
<Divider></Divider>
<Button icon={<QuestionCircleFilled/>} type="link" target="_blank"
href="https://netbird.io/docs/overview/setup-keys"
style={{color: 'rgb(07, 114, 128)'}}>Learn
more about setup keys</Button>
href="https://netbird.io/docs/overview/setup-keys">Learn more about setup keys</Button>
</Col>
</Row>
</Form>
+6 -6
View File
@@ -9,10 +9,10 @@ import {FormUser, User, UserToSave} from "../store/user/types";
import {RuleObject} from "antd/lib/form";
import {CustomTagProps} from "rc-select/lib/BaseSelect";
import {actions as userActions} from "../store/user";
import {useGetAccessTokenSilently} from "../utils/token";
import {useGetTokenSilently} from "../utils/token";
import {useOidcUser} from "@axa-fr/react-oidc";
const {Paragraph, Text} = Typography;
const {Paragraph} = Typography;
const {confirm} = Modal;
@@ -20,7 +20,7 @@ const {Option} = Select;
const UserUpdate = () => {
const {oidcUser} = useOidcUser();
const {getAccessTokenSilently} = useGetAccessTokenSilently()
const {getTokenSilently} = useGetTokenSilently()
const dispatch = useDispatch()
const user = useSelector((state: RootState) => state.user.user)
const savedUser = useSelector((state: RootState) => state.user.savedUser)
@@ -193,7 +193,7 @@ const UserUpdate = () => {
okType: 'danger',
onOk() {
dispatch(userActions.saveUser.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: userToSave
}))
},
@@ -215,7 +215,7 @@ const UserUpdate = () => {
showConfirmChangeRole(userToSave)
} else {
dispatch(userActions.saveUser.request({
getAccessTokenSilently: getAccessTokenSilently,
getAccessTokenSilently: getTokenSilently,
payload: userToSave
}))
}
@@ -358,7 +358,7 @@ const UserUpdate = () => {
>
<Select
style={{width: '100%'}}
disabled={currentUser.role != null && currentUser.role !== "admin"}>
disabled={(currentUser.role != null && currentUser.role !== "admin") || formUser.is_current}>
<Option value="admin">admin</Option>
<Option value="user">user</Option>
</Select>
+100
View File
@@ -0,0 +1,100 @@
import React, {useState} from 'react';
import {Tabs, TabsProps} from "antd";
import Icon, {AndroidFilled, AppleFilled, WindowsFilled} from "@ant-design/icons";
import {ReactComponent as LinuxSVG} from "../icons/terminal_icon.svg";
import UbuntuTab from "./UbuntuTab";
import {ReactComponent as DockerSVG} from "../icons/docker_icon.svg";
import Paragraph from "antd/lib/typography/Paragraph";
import WindowsTab from "./WindowsTab";
import MacTab from "./MacTab";
import Link from "antd/lib/typography/Link";
import DockerTab from "./DockerTab";
type Props = {
greeting?: string;
headline: string;
};
const detectOSTab = () => {
let os = 1;
if (navigator.userAgent.indexOf("Win") !== -1) os = 2;
if (navigator.userAgent.indexOf("Mac") !== -1) os = 3;
if (navigator.userAgent.indexOf("X11") !== -1) os = 1;
if (navigator.userAgent.indexOf("Linux") !== -1) os = 1
return os
}
export const AddPeerPopup: React.FC<Props> = ({
greeting,
headline,
}) => {
const [openTab, setOpenTab] = useState(detectOSTab);
const [width, setWidth] = useState<number>(window.innerWidth);
const isMobile = width <= 768;
const items: TabsProps['items'] = [
{
key: "1",
label: <span><Icon component={LinuxSVG}/>Linux</span>,
children: <UbuntuTab/>,
},
{
key: "2",
label: <span><WindowsFilled/>Windows</span>,
children: <WindowsTab/>,
},
{
key: "3",
label: <span><AppleFilled/>macOS</span>,
children: <MacTab/>,
},
/*{
key: "4",
label: <span><AndroidFilled/>Android</span>,
children: <></>,
},*/
{
key: "5",
label: <span><Icon component={DockerSVG}/>Docker</span>,
children: <DockerTab/>,
}
];
return <>
{greeting && <Paragraph
style={{textAlign: "center", whiteSpace: "pre-line", fontSize: "2em", marginBottom: -10}}>
{greeting}
</Paragraph>}
<Paragraph
style={{textAlign: "center", whiteSpace: "pre-line", fontSize: "2em"}}>
{headline}
</Paragraph>
<Paragraph type={"secondary"}
style={{
marginTop: "-15px",
textAlign: "center",
whiteSpace: "pre-line",
}}>
To get started install NetBird and log in using your {"\n"} email account.
</Paragraph>
<Tabs centered={!isMobile}
defaultActiveKey={openTab.toString()} tabPosition="top" animated={{inkBar: true, tabPane: false}}
items={items}/>
<Paragraph type={"secondary"}
style={{
marginTop: "15px",
}}>
After that you should be connected. Add more devices to your network or manage your existing devices in the
admin panel.
If you have further questions check out our {<Link target="_blank"
href={"https://netbird.io/docs/getting-started/installation"}>installation
guide</Link>}
</Paragraph>
</>
}
export default AddPeerPopup
+63
View File
@@ -0,0 +1,63 @@
import React, {useState} from 'react';
import {StepCommand} from "./types"
import {formatDockerCommand, formatNetBirdUP} from "./common"
import SyntaxHighlighter from "react-syntax-highlighter";
import TabSteps from "./TabSteps";
import {Button, Typography} from "antd";
import Link from "antd/lib/typography/Link";
const {Title, Paragraph, Text} = Typography;
export const DockerTab = () => {
const [steps, setSteps] = useState([
{
key: 1,
title: 'Install Docker',
commands: (
<Button style={{marginTop: "5px"}} type="primary" href="https://docs.docker.com/engine/install/" target="_blank">Official Docker website</Button>
),
copied: false,
showCopyButton: false
} as StepCommand,
{
key: 2,
title: 'Run NetBird container',
commands: formatDockerCommand(),
copied: false,
showCopyButton: false
} as StepCommand,
{
key: 3,
title: "Read docs",
commands: (
<Link href="https://netbird.io/docs/getting-started/installation#running-netbird-in-docker" target="_blank">Running NetBird in Docker</Link>
),
copied: false,
showCopyButton: false
} as StepCommand
])
return (
<div style={{marginTop: 10}}>
{/*<Text style={{fontWeight: "bold"}}>
Run in Docker
</Text>
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
<SyntaxHighlighter language="bash">
{formatDockerCommand()}
</SyntaxHighlighter>
</div>*/}
<Text style={{fontWeight: "bold"}}>
Install on Ubuntu
</Text>
<div style={{marginTop: 5}}>
<TabSteps stepsItems={steps}/>
</div>
</div>
)
}
export default DockerTab
+71 -12
View File
@@ -1,18 +1,50 @@
import React, {useState} from 'react';
import { Button } from "antd";
import {Button, Typography} from "antd";
import TabSteps from "./TabSteps";
import { StepCommand } from "./types"
import { formatNetBirdUP } from "./common"
import {StepCommand} from "./types"
import {formatNetBirdUP} from "./common"
import {Collapse} from "antd";
import SyntaxHighlighter from "react-syntax-highlighter";
const { Panel } = Collapse;
const {Text} = Typography;
export const LinuxTab = () => {
const [quickSteps, setQuickSteps] = useState([
{
key: 1,
title: 'Download and run installer:',
commands: (
<Button style={{marginTop: "5px"}} type="primary" href="https://pkgs.netbird.io/windows/x64"
target="_blank">Download NetBird</Button>
),
copied: false
} as StepCommand,
{
key: 2,
title: 'Click on "Connect" from the NetBird icon in your system tray',
commands: '',
copied: false,
showCopyButton: false
},
{
key: 3,
title: 'Sign up using your email address',
commands: '',
copied: false,
showCopyButton: false
}
])
const [steps, setSteps] = useState([
{
key: 1,
title: 'Download and install Brew (package manager)',
title: 'Download and install Homebrew',
commands: (
<Button type="primary" href="https://brew.sh/" target="_blank">Download Brew</Button>
<Button style={{marginTop: "5px"}} type="primary" href="https://brew.sh/" target="_blank">Download
Brew</Button>
),
copied: false
} as StepCommand,
@@ -30,24 +62,51 @@ export const LinuxTab = () => {
} as StepCommand,
{
key: 3,
title: 'Run NetBird and log in the browser:',
commands: formatNetBirdUP(),
title: 'Start NetBird daemon:',
commands: [
`sudo netbird service install`,
`sudo netbird service start`
].join('\n'),
copied: false,
showCopyButton: true
} as StepCommand,
{
key: 4,
title: 'Get your IP address:',
commands: [
`sudo ifconfig utun100`
].join('\n'),
title: 'Run NetBird and log in the browser:',
commands: formatNetBirdUP(),
copied: false,
showCopyButton: true
} as StepCommand
])
return (
<TabSteps stepsItems={steps}/>
<div style={{marginTop: 10}}>
<Text style={{fontWeight: "bold"}}>
Install with one command
</Text>
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
<SyntaxHighlighter language="bash">
curl -fsSL https://pkgs.netbird.io/install.sh | sh
</SyntaxHighlighter>
</div>
<Text style={{fontWeight: "bold"}}>
Or install manually with HomeBrew
</Text>
<div style={{marginTop: 5}}>
<TabSteps stepsItems={steps}/>
</div>
</div>
/*<div style={{marginTop: 5}}>
<TabSteps stepsItems={quickSteps}/>
</div>*/
/*<div style={{marginTop: 10}}>
<Text style={{fontWeight: "bold"}}>
Install on macOS with Homebrew
</Text>
<div style={{marginTop: 5}}>
<TabSteps stepsItems={steps}/>
</div>
</div>*/
)
}
+7 -20
View File
@@ -2,16 +2,16 @@ import "highlight.js/styles/mono-blue.css";
import "highlight.js/lib/languages/bash";
import { StepCommand } from './types'
import SyntaxHighlighter from 'react-syntax-highlighter';
import { monoBlue } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import {
Typography,
Space,
Steps, Button
Steps, Button, Popover, StepsProps
} from "antd";
import {copyToClipboard} from "../../utils/common";
import {CheckOutlined, CopyOutlined} from "@ant-design/icons";
import React, {useEffect, useState} from "react";
const { Step } = Steps;
const {Text} = Typography;
type Props = {
stepsItems: Array<StepCommand>
@@ -36,35 +36,22 @@ const TabSteps:React.FC<Props> = ({stepsItems}) => {
}, 2000)
}
}
return (
<Steps direction="vertical" current={0}>
<Steps direction="vertical" size={"small"}>
{steps.map(c =>
<Step
status={"process"}
key={c.key}
title={c.title}
title={<Text>{c.title}</Text>}
description={
<Space className="nb-code" direction="vertical" size="small" style={{display: "flex"}}>
<Space className="nb-code" direction="vertical" size="small" style={{display: "flex", fontSize: ".85em"}}>
{ (c.commands && (typeof c.commands === 'string')) ? (
<SyntaxHighlighter language="bash" style={monoBlue}>
<SyntaxHighlighter language="bash">
{c.commands}
</SyntaxHighlighter>
) : (
c.commands
)}
{ c.showCopyButton &&
<>
{ !c.copied ? (
<Button type="text" size="large" className="btn-copy-code" icon={<CopyOutlined/>}
style={{color: "rgb(107, 114, 128)"}}
onClick={() => onCopyClick(c.key, c.commands, true)}/>
): (
<Button type="text" size="large" className="btn-copy-code" icon={<CheckOutlined/>}
style={{color: "green"}}/>
)}
</>
}
</Space>
}
/>
+31 -36
View File
@@ -1,31 +1,29 @@
import React, { useState } from 'react';
import { Button } from "antd";
import React, {useState} from 'react';
import {StepCommand} from "./types"
import {formatNetBirdUP} from "./common"
import SyntaxHighlighter from "react-syntax-highlighter";
import TabSteps from "./TabSteps";
import { StepCommand } from "./types"
import { getConfig } from "../../config";
import Paragraph from 'antd/lib/skeleton/Paragraph';
import { formatNetBirdUP } from "./common"
import {Typography} from "antd";
const {Title, Paragraph, Text} = Typography;
export const UbuntuTab = () => {
const [steps, setSteps] = useState([
{
key: 1,
title: 'Add repository:',
title: 'Add repository',
commands: [
`sudo apt install ca-certificates curl gnupg -y`,
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`
].join('\n'),
copied: false,
showCopyButton: true
showCopyButton: false
} as StepCommand,
{
key: 2,
title: 'Install NetBird:',
title: 'Install NetBird',
commands: [
`sudo apt-get update`,
`# for CLI only`,
@@ -34,39 +32,36 @@ export const UbuntuTab = () => {
`sudo apt-get install netbird-ui`
].join('\n'),
copied: false,
showCopyButton: true
showCopyButton: false
} as StepCommand,
{
key: 3,
title: 'Run NetBird and log in the browser:',
title: 'Run NetBird and log in the browser',
commands: formatNetBirdUP(),
copied: false,
showCopyButton: true
} as StepCommand,
{
key: 4,
title: 'Get your IP address:',
commands: [
`ip addr show wt0`
].join('\n'),
copied: false,
showCopyButton: true
} as StepCommand,
showCopyButton: false
} as StepCommand
])
/*const clickTest = () => {
steps.push({
key: steps.length+1,
title: `Test ${steps.length+1}`,
commands: [`hi lorena!`].join('\n'),
copied: false
})
console.log(steps)
setSteps([...steps])
}*/
return (
<TabSteps stepsItems={steps} />
<div style={{marginTop: 10}}>
<Text style={{fontWeight: "bold"}}>
Install with one command
</Text>
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
<SyntaxHighlighter language="bash">
curl -fsSL https://pkgs.netbird.io/install.sh | sh
</SyntaxHighlighter>
</div>
<Text style={{fontWeight: "bold"}}>
Or install manually on Ubuntu
</Text>
<div style={{marginTop: 5}}>
<TabSteps stepsItems={steps}/>
</div>
</div>
)
}
+14 -8
View File
@@ -1,33 +1,31 @@
import React, {useState} from 'react';
import { Button } from "antd";
import {Button, Typography} from "antd";
import TabSteps from "./TabSteps";
import { StepCommand } from "./types"
import {getConfig} from "../../config";
const {latestVersion} = getConfig();
const {Text} = Typography;
export const WindowsTab = () => {
const releaseVersion = latestVersion ? latestVersion.replace("v", "") : "0.6.3"
const [steps, setSteps] = useState([
{
key: 1,
title: 'Download and run Windows installer:',
commands: (
<Button type="primary" href={`https://github.com/netbirdio/netbird/releases/download/v${releaseVersion}/netbird_installer_${releaseVersion}_windows_amd64.exe`} target="_blank">Download NetBird</Button>
<Button style={{marginTop: "5px"}} type="primary" href="https://pkgs.netbird.io/windows/x64" target="_blank">Download NetBird</Button>
),
copied: false
} as StepCommand,
{
key: 2,
title: 'Click on "Connect" from the NetBird icon in your system tray.',
title: 'Click on "Connect" from the NetBird icon in your system tray',
commands: '',
copied: false,
showCopyButton: false
},
{
key: 3,
title: 'Log in your browser.\n',
title: 'Sign up using your email address',
commands: '',
copied: false,
showCopyButton: false
@@ -35,7 +33,15 @@ export const WindowsTab = () => {
])
return (
<TabSteps stepsItems={steps}/>
<div style={{marginTop: 10}}>
<Text style={{fontWeight: "bold"}}>
Install on Windows
</Text>
<div style={{marginTop: 5}}>
<TabSteps stepsItems={steps}/>
</div>
</div>
)
}
+14 -2
View File
@@ -3,11 +3,23 @@ const { grpcApiOrigin } = getConfig();
export const formatNetBirdUP = () => {
let cmd = "sudo netbird up"
let cmd = "netbird up"
if (grpcApiOrigin) {
cmd = "sudo netbird up --management-url " + grpcApiOrigin
cmd = "netbird up --management-url " + grpcApiOrigin
}
return [
cmd
].join('\n')
}
export const formatDockerCommand = () => {
let cmd = ["docker run --rm -d",
" --cap-add=NET_ADMIN",
" -e NB_SETUP_KEY=SETUP_KEY",
" -v netbird-client:/etc/netbird"]
if (grpcApiOrigin) {
cmd.push(" -e NB_MANAGEMENT_URL="+grpcApiOrigin)
}
cmd.push(" netbirdio/netbird:latest")
return cmd.join(' \\\n')
}
Loaded 20 of 61 files, more files were not shown because too many files have changed in this diff. Show more