Compare commits

..
Author SHA1 Message Date
Sarooj Bukhari 8ad6952dae da 2023-08-25 14:44:10 +05:00
Sarooj Bukhari 7e5595c365 fix button hover 2023-08-15 21:39:36 +05:00
Sarooj Bukhari 12687379cc limit user account 2023-08-15 21:04:52 +05:00
braginini 62d80af186 Limit columns and navbar 2023-08-15 15:37:37 +02:00
54 changed files with 1960 additions and 4054 deletions

No files matched your search

-15
View File
@@ -1,15 +0,0 @@
name: Codespell
on: [pull_request]
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: codespell
uses: codespell-project/actions-codespell@v2
with:
only_warn: 1
skip: package-lock.json,*.svg
+3 -3
View File
@@ -1,15 +1,15 @@
# Wiretrustee Dashboard
Wiretrustee Dashboard is a the Wiretrustee Management server UI. It allow users to signin, view setup keys and manage peers. This image is **not ready** for production use.
Wiretrustee Dashboard is a the Wiretrustee Managemenet server UI. It allow users to signin, view setup keys and manage peers. This image is **not ready** for production use.
## Tags
```latest``` ```vX.X.X``` not available yet.
```main``` built on every PR being merged to the repository
```main``` builded on every PR being merged to the repository
## How to use this image
HTTP run:
```shell
docker run -d --rm -p 80:80 wiretrustee/dashboard:main
```
Using SSL certificate from Let's Encrypt®:
Using SSL certificate from Let's Encript®:
```shell
docker run -d --rm -p 80:80 -p 443:443 \
-e LETSENCRYPT_DOMAIN=app.mydomain.com \
+1 -1
View File
@@ -387,7 +387,7 @@ check_nb_domain() {
if [ "$DOMAIN" == "netbird.example.com" ]; then
echo "The NETBIRD_DOMAIN cannot be netbird.example.com" > /dev/stderr
return 1
retrun 1
fi
return 0
}
-56
View File
@@ -1,56 +0,0 @@
import { Page, test, expect } from "@playwright/test";
export class AccessControlPage {
private readonly accessControlUrl = 'http://localhost:3000/acls'
private readonly defaulAccessControl = this.page.getByRole('cell', { name: 'Default' })
private readonly deleteButton = this.page.getByRole('button', { name: 'Delete' })
private readonly deleteModal = this.page.getByTestId('confirm-delete-modal-title')
private readonly confirmButton = this.page.getByRole('button', { name: 'OK' })
private readonly addRulesButton = this.page.getByTestId('add-rule-empty-state-button')
constructor(private readonly page: Page) {}
async openAccessControlPage() {
await test.step('Open Access Control page', async () => {
await this.page.goto(this.accessControlUrl);
})
}
async assertDefaultAccessCotrolIsCreated() {
await test.step('Assert that default control access is created', async () => {
await expect(this.defaulAccessControl).toBeVisible();
})
}
async pressDeleteButton() {
await test.step('Press delete button', async () => {
await this.deleteButton.click();
})
}
async assertDeleteModalIsVisibile() {
await test.step('Assert access control deletion modal is visible', async () => {
await expect(this.deleteModal).toBeVisible();
})
}
async pressConfirmButton() {
await test.step('Press confirm button on access control deletion modal', async () => {
await this.confirmButton.click();
})
}
async assertDefaultAccessCotrolIsDeleted() {
await test.step('Assert default access control should be deleted', async () => {
await expect(this.defaulAccessControl).not.toBeVisible();
})
}
async assertAddRuleButtonIsVisile() {
await test.step('Assert Add Rules button is visible', async () => {
await expect(this.addRulesButton).toBeVisible();
})
}
}
export default AccessControlPage;
-34
View File
@@ -1,34 +0,0 @@
import { Page, test, expect} from "@playwright/test";
export class LoginPage {
private readonly localUrl = 'http://localhost:3000/'
private readonly usernameField = this.page.getByPlaceholder('username@domain')
private readonly nextButton = this.page.getByRole('button', { name: 'next' })
private readonly passwordField = this.page.getByLabel('Password')
private readonly skipButton = this.page.getByRole('button', { name: 'skip' });
private readonly netBirdLogo = this.page.getByRole('link', { name: 'logo' })
constructor(private readonly page: Page) {}
async doLogin() {
await test.step('Login to local enviroment', async () => {
await this.page.goto(this.localUrl);
await this.usernameField.fill('admin@localhost');
await this.pressNextButton();
await this.passwordField.fill('testMe123@');
await this.pressNextButton();
if (await this.skipButton.isVisible({ timeout: 300 })) {
await this.skipButton.click();
}
await expect(this.netBirdLogo).toBeVisible();
})
}
async pressNextButton() {
await test.step('Press next button', async () => {
await this.nextButton.click();
})
}
}
export default LoginPage;
+1 -1
View File
@@ -1,4 +1,4 @@
import { Page, test } from "@playwright/test";
import { Page, test, expect } from "@playwright/test";
export class PeersPage {
private readonly addNewPeerButton = this.page.getByTestId('add-new-peer-button')
+26 -15
View File
@@ -1,22 +1,33 @@
import { test } from '@playwright/test'
import {LoginPage} from '../pages/login-page'
import {AccessControlPage} from '../pages/access-control-page'
import { test, expect } from '@playwright/test';
import {AddPeerModal} from '../pages/modals/add-peer-modal'
import {TopMenu} from '../pages/top-menu';
let loginPage: LoginPage
let accessControlPage: AccessControlPage
const URL = 'https://app.netbird.io/'
const localUrl = 'http://localhost:3000/'
let addPeerModal: AddPeerModal;
let topMenu: TopMenu;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
await loginPage.doLogin();
addPeerModal = new AddPeerModal(page);
await page.goto(localUrl);
await page.getByPlaceholder('username@domain').fill('admin@localhost');
await page.getByRole('button', { name: 'next' }).click();
await page.getByLabel('Password').fill('testMe123@');
await page.getByRole('button', { name: 'next' }).click();
const skipButton = page.getByRole('button', { name: 'skip' });
if (await skipButton.isVisible({ timeout: 300 })) {
await skipButton.click();
}
await addPeerModal.assertPeerModalIsVisible();
});
test('Confirm that new user has Default access', async ({ page }) => {
accessControlPage = new AccessControlPage(page);
await accessControlPage.openAccessControlPage();
await accessControlPage.assertDefaultAccessCotrolIsCreated();
await accessControlPage.pressDeleteButton();
await accessControlPage.assertDeleteModalIsVisibile();
await accessControlPage.pressConfirmButton();
await accessControlPage.assertDefaultAccessCotrolIsDeleted();
await accessControlPage.assertAddRuleButtonIsVisile();
topMenu = new TopMenu(page);
await addPeerModal.closeAddPeerModal();
await addPeerModal.assertPeerModalIsNotVisible();
await topMenu.clickOnAccessControlOnTopMenu();
await expect(page.getByRole('cell', { name: 'Default' })).toBeVisible();
await page.getByRole('button', { name: 'Delete' }).click();
await expect(page.getByTestId('confirm-delete-modal-title')).toBeVisible();
await page.getByRole('button', { name: 'Cancel' }).click();
});
+19 -12
View File
@@ -1,41 +1,48 @@
import { test } from '@playwright/test'
import { test } from '@playwright/test';
import {AddPeerModal} from '../pages/modals/add-peer-modal'
import {PeersPage} from '../pages/peers-page'
import {LoginPage} from '../pages/login-page'
let addPeerModal: AddPeerModal
let peersPage: PeersPage
let loginPage: LoginPage
const URL = 'https://app.netbird.io/'
const localUrl = 'http://localhost:3000/'
let addPeerModal: AddPeerModal;
let peersPage: PeersPage;
test.beforeEach(async ({ page }) => {
addPeerModal = new AddPeerModal(page);
loginPage = new LoginPage(page);
await loginPage.doLogin();
await page.goto(localUrl);
await page.getByPlaceholder('username@domain').fill('admin@localhost');
await page.getByRole('button', { name: 'next' }).click();
await page.getByLabel('Password').fill('testMe123@');
await page.getByRole('button', { name: 'next' }).click();
const skipButton = page.getByRole('button', { name: 'skip' });
if (await skipButton.isVisible({ timeout: 300 })) {
await skipButton.click();
}
await addPeerModal.assertPeerModalIsVisible();
});
test('Test Linux tab on a first access add peer modal / @bc', async function () {
test('Test Linux tab on a first access add peer modal / @bc', async function ({ }) {
await addPeerModal.openLinuxTab();
await addPeerModal.assertLinuxTabHasCorrectText();
});
test('Test Windows tab on a first access add peer modal / @bc', async () => {
test('Test Windows tab on a first access add peer modal / @bc', async ({ }) => {
await addPeerModal.openWindowsTab();
await addPeerModal.assertWindowsDownloadButtonHasCorrectLink();
});
test('Test MacOS tab on a first access add peer modal / @bc', async () => {
test('Test MacOS tab on a first access add peer modal / @bc', async ({ }) => {
await addPeerModal.openMacTab();
await addPeerModal.assertIntelDownloadButtonHasCorrectLink();
await addPeerModal.assertM1M2DownloadButtonHasCorrectLink();
});
test('Test Android tab on a first access add peer modal', async () => {
test('Test Android tab on a first access add peer modal', async ({ }) => {
await addPeerModal.openAndroidTab();
await addPeerModal.assertAndroidDownloadButtonHasCorrectLink();
});
test('Test Docker tab on a first access add peer modal', async () => {
test('Test Docker tab on a first access add peer modal', async ({ }) => {
await addPeerModal.openDockerTab();
await addPeerModal.assertDockerDownloadButtonHasCorrectLink();
});
+39 -6
View File
@@ -12,6 +12,8 @@
"@axa-fr/react-oidc": "^5.14.0",
"@headlessui/react": "^1.5.0",
"@heroicons/react": "^1.0.4",
"@stripe/react-stripe-js": "^2.1.2",
"@stripe/stripe-js": "^2.1.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
@@ -63,9 +65,9 @@
}
},
"node_modules/@adobe/css-tools": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz",
"integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg=="
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
"integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g=="
},
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
@@ -3405,6 +3407,24 @@
"@sinonjs/commons": "^1.7.0"
}
},
"node_modules/@stripe/react-stripe-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.1.2.tgz",
"integrity": "sha512-2JcKRvOdMD698uGrY0cINLHb6XHnY24L+wlWlw2+TbI2aXlQZ9t8Mc+KssFES+EuNgISbrp7fJR3w/13epfiOw==",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"@stripe/stripe-js": "^1.44.1 || ^2.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@stripe/stripe-js": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.1.0.tgz",
"integrity": "sha512-AdMHWqIq/s3TLmoyuzdb6l21STKDKNm82cjce1VelyQGinVNjVgSEPH5UDtxENEP7OzgMZH8RTphFMi0fMJGLQ=="
},
"node_modules/@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -17173,9 +17193,9 @@
},
"dependencies": {
"@adobe/css-tools": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz",
"integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg=="
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz",
"integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g=="
},
"@ampproject/remapping": {
"version": "2.2.0",
@@ -19477,6 +19497,19 @@
"@sinonjs/commons": "^1.7.0"
}
},
"@stripe/react-stripe-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.1.2.tgz",
"integrity": "sha512-2JcKRvOdMD698uGrY0cINLHb6XHnY24L+wlWlw2+TbI2aXlQZ9t8Mc+KssFES+EuNgISbrp7fJR3w/13epfiOw==",
"requires": {
"prop-types": "^15.7.2"
}
},
"@stripe/stripe-js": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.1.0.tgz",
"integrity": "sha512-AdMHWqIq/s3TLmoyuzdb6l21STKDKNm82cjce1VelyQGinVNjVgSEPH5UDtxENEP7OzgMZH8RTphFMi0fMJGLQ=="
},
"@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
+4 -2
View File
@@ -7,6 +7,8 @@
"@axa-fr/react-oidc": "^5.14.0",
"@headlessui/react": "^1.5.0",
"@heroicons/react": "^1.0.4",
"@stripe/react-stripe-js": "^2.1.2",
"@stripe/stripe-js": "^2.1.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
@@ -79,7 +81,7 @@
]
},
"devDependencies": {
"@types/react-syntax-highlighter": "^15.5.3",
"@playwright/test": "^1.36.2"
"@playwright/test": "^1.36.2",
"@types/react-syntax-highlighter": "^15.5.3"
}
}
+9 -9
View File
@@ -39,15 +39,15 @@ export default defineConfig({
use: { channel: 'chrome', },
},
// {
// name: 'firefox',
// use: { browserName: 'firefox', },
// },
//
// {
// name: 'webkit',
// use: { browserName: 'webkit', },
// },
{
name: 'firefox',
use: { browserName: 'firefox', },
},
{
name: 'webkit',
use: { browserName: 'webkit', },
},
/* Test against mobile viewports. */
// {
+11
View File
@@ -0,0 +1,11 @@
{
"auth0Auth": "true",
"authAuthority": "https://netbird-localdev.eu.auth0.com",
"authClientId": "kBRMAOqIZ7hvpVCaypQLCJvTzkYYIXVt",
"authScopesSupported": "openid profile email api offline_access email_verified",
"authAudience": "http://localhost:3000/",
"apiOrigin": "http://0.0.0.0",
"grpcApiOrigin": "http://0.0.0.0:80",
"latestVersion": "v0.6.3"
}
+1 -1
View File
@@ -284,7 +284,7 @@ const AccessControlNew = () => {
<Divider style={{ margin: "8px 0" }} />
<Row style={{ padding: "0 8px 4px" }}>
<Col flex="auto">
<Text type={"secondary"}>Add new groups by pressing "Enter"</Text>
<Text type={"secondary"}>Add new ports by pressing "Enter"</Text>
</Col>
<Col flex="none">
<svg
View File
Whitespace-only changes.
+1 -57
View File
@@ -73,7 +73,6 @@ const NameServerGroupAdd = () => {
const inputNameRef = useRef<any>(null);
const inputDescriptionRef = useRef<any>(null);
const [selectCustom, setSelectCustom] = useState(false);
const [matchDomains, setMatchDomains] = useState(0);
useEffect(() => {
if (editName)
@@ -88,6 +87,7 @@ const NameServerGroupAdd = () => {
cursor: "end",
});
}, [editDescription]);
useEffect(() => {
if (!nsGroup) return;
@@ -117,7 +117,6 @@ const NameServerGroupAdd = () => {
nameservers: [] as NameServer[],
groups: [],
enabled: false,
search_domains_enabled: false,
} as NameServerGroup)
);
setEditName(false);
@@ -126,7 +125,6 @@ const NameServerGroupAdd = () => {
};
const onChange = (changedValues: any) => {
if (changedValues.primary !== undefined) {
setIsPrimary(changedValues.primary);
}
@@ -158,7 +156,6 @@ const NameServerGroupAdd = () => {
],
groups: [],
enabled: true,
search_domains_enabled: false,
},
{
name: cloudflareChoice,
@@ -179,7 +176,6 @@ const NameServerGroupAdd = () => {
],
groups: [],
enabled: true,
search_domains_enabled: false,
},
{
name: quad9Choice,
@@ -200,7 +196,6 @@ const NameServerGroupAdd = () => {
],
groups: [],
enabled: true,
search_domains_enabled: false,
},
];
@@ -208,7 +203,6 @@ const NameServerGroupAdd = () => {
let nsGroupLocal = {} as NameServerGroup;
if (value === customChoice) {
nsGroupLocal = nsGroup;
nsGroupLocal.domains = [];
} else {
defaultDNSOptions.forEach((nsg) => {
if (value === nsg.name) {
@@ -268,8 +262,6 @@ const NameServerGroupAdd = () => {
groups: existingGroups,
groupsToCreate: newGroups,
enabled: values.enabled,
search_domains_enabled:
matchDomains > 0 ? formNSGroup.search_domains_enabled : false,
} as NameServerGroupToSave;
};
@@ -470,7 +462,6 @@ const NameServerGroupAdd = () => {
</Col>
</Space>
</Row>
{setMatchDomains(fields.length)}
{fields.map((field, index) => {
return (
<Row key={index} style={{ marginBottom: "5px" }}>
@@ -531,13 +522,6 @@ const NameServerGroupAdd = () => {
});
};
const handleChangeMarkDomain = (checked: boolean) => {
setFormNSGroup({
...formNSGroup,
search_domains_enabled: checked,
});
};
return (
<>
{nsGroup && (
@@ -717,46 +701,6 @@ const NameServerGroupAdd = () => {
<Col span={24}>
<Form.List name="domains">{renderDomains}</Form.List>
</Col>
{matchDomains > 0 && (
<Col span={24}>
<Form.Item name="search_domains_enabled" label="">
<div
style={{
display: "flex",
gap: "15px",
}}
>
<Switch
onChange={handleChangeMarkDomain}
size="small"
/>
<div>
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "500",
}}
>
Mark match domains as search domains
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "0",
}}
>
E.g., "peer.example.com" will be accessible with
"peer"
</Paragraph>
</div>
</div>
</Form.Item>
</Col>
)}
<Col span={24}>
<label
style={{
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -161,7 +161,7 @@ const Navbar = () => {
return false;
}
if (user.role?.toLowerCase() === "admin" || user.role?.toLowerCase() === "owner") {
if (user.role?.toLowerCase() === "admin") {
return true;
}
return !adminOnlyTabs.find((t) => t === key);
+125 -209
View File
@@ -35,7 +35,6 @@ import {
LockOutlined,
EditOutlined,
ExclamationCircleOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import { RuleObject } from "antd/lib/form";
import { useGetTokenSilently } from "../utils/token";
@@ -146,22 +145,10 @@ const PeerUpdate = (props: any) => {
useEffect(() => {
setPeerRoutes([]);
const temp: any[] = [];
if (peer && peer.groups) {
peer?.groups?.forEach((pg: any) => {
routes.forEach((route: any) => {
if (route.peer_groups?.includes(pg.id)) {
temp.push(route);
}
});
});
}
const filterPeerRoutes: any = routes.filter(
(route) => route.peer === peer.id
);
let mergeArr: any = [...filterPeerRoutes, ...temp];
setPeerRoutes(mergeArr);
setPeerRoutes(filterPeerRoutes);
const filterNotPeerRoutes: any = routes.filter(
(route) => route.peer !== peer.id
);
@@ -192,16 +179,6 @@ const PeerUpdate = (props: any) => {
useEffect(() => {}, [users]);
const routeAddAllowed = (os: string): boolean => {
return (
os !== "" &&
!os.toLowerCase().startsWith("darwin") &&
!os.toLowerCase().startsWith("windows") &&
!os.toLowerCase().startsWith("ios") &&
!os.toLowerCase().startsWith("android")
);
};
const toggleEditName = (status: boolean, value?: string) => {
setEditName(status);
@@ -412,13 +389,13 @@ const PeerUpdate = (props: any) => {
const style = { marginTop: 85 };
if (savedGroups.loading) {
message.loading({
content: "Updating peer group...",
content: "Updating peer groups...",
key: saveGroupsKey,
style,
});
} else if (savedGroups.success) {
message.success({
content: "Peer group have been successfully updated.",
content: "Peer groups have been successfully updated.",
key: saveGroupsKey,
duration: 2,
style,
@@ -431,7 +408,7 @@ const PeerUpdate = (props: any) => {
} else if (savedGroups.error) {
message.error({
content:
"Failed to update peer group. You might not have enough permissions.",
"Failed to update peer groups. You might not have enough permissions.",
key: saveGroupsKey,
duration: 2,
style,
@@ -675,48 +652,6 @@ const PeerUpdate = (props: any) => {
}
}, [deletedRoute]);
const renderGroupRouting = (rowGroups: string[] | null) => {
let groupsMap = new Map<string, Group>();
groups.forEach((g) => {
groupsMap.set(g.id!, g);
});
let displayGroups: Group[] = [];
if (rowGroups) {
displayGroups = rowGroups
.filter((g) => groupsMap.get(g))
.map((g) => groupsMap.get(g)!);
}
const groupToCompare =
peer &&
peer.groups &&
peer?.groups.map((element1) => {
return element1.id;
});
return (
<div className="gp-main-wrapper">
{displayGroups &&
displayGroups.length > 0 &&
displayGroups.map((group) => {
if (group.id && !groupToCompare?.includes(group?.id)) {
return null;
}
return (
<div className="g-r-wrapper">
<span className="f-r-name">
<Tag color={"blue"} style={{ marginRight: 3 }}>
{group.name}
</Tag>
</span>{" "}
</div>
);
})}
</div>
);
};
return (
<>
{peer && (
@@ -1030,151 +965,132 @@ const PeerUpdate = (props: any) => {
{/* --- */}
{!isGroupUpdateView && (
<>
{routeAddAllowed(peer.os) && (
<Card
bordered={true}
// loading={loading}Ć’
style={{ marginBottom: "7px" }}
>
<div style={{ maxWidth: "800px" }}>
<Paragraph
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontSize: "16px",
fontWeight: "500",
}}
<Card
bordered={true}
// loading={loading}Ć’
style={{ marginBottom: "7px" }}
>
<div style={{ maxWidth: "800px" }}>
<Paragraph
style={{
textAlign: "left",
whiteSpace: "pre-line",
fontSize: "16px",
fontWeight: "500",
}}
>
Network routes
</Paragraph>
<Row
gutter={21}
style={{ marginTop: "-16px", marginBottom: "10px" }}
>
<Col
xs={24}
sm={24}
md={20}
lg={20}
xl={20}
xxl={20}
span={20}
>
Network routes
</Paragraph>
<Row
gutter={21}
style={{ marginTop: "-16px", marginBottom: "10px" }}
<Paragraph
type={"secondary"}
style={{ textAlign: "left", whiteSpace: "pre-line" }}
>
Access other networks without installing NetBird on
every resource.
</Paragraph>
</Col>
<Col
xs={24}
sm={24}
md={1}
lg={1}
xl={1}
xxl={1}
span={1}
style={{ marginTop: "-16px" }}
>
<Col
xs={24}
sm={24}
md={20}
lg={20}
xl={20}
xxl={20}
span={20}
>
<Paragraph
type={"secondary"}
style={{ textAlign: "left", whiteSpace: "pre-line" }}
>
Access other networks without installing NetBird on
every resource.
</Paragraph>
</Col>
<Col
xs={24}
sm={24}
md={1}
lg={1}
xl={1}
xxl={1}
span={1}
style={{ marginTop: "-16px" }}
>
{peerRoutes && peerRoutes.length > 0 && (
<Button type="primary" onClick={onClickAddNewRoute}>
Add route
</Button>
)}
</Col>
</Row>
{peerRoutes && peerRoutes.length > 0 && (
<Table
size={"small"}
style={{ marginTop: "-10px" }}
showHeader={false}
scroll={{ x: 800 }}
pagination={false}
dataSource={peerRoutes}
>
<Column title="Name" dataIndex="network_id" />
<Column title="Name" dataIndex="network" />
<Column
title="enabled"
dataIndex="network"
render={(e, record: any, index) => {
return record.peer_groups ? (
renderGroupRouting(record.peer_groups)
) : (
<>
<Switch
defaultChecked={record.enabled}
size="small"
onChange={(checked) =>
onRouteEnableChange(checked, record)
}
/>
</>
);
}}
/>
<Column
align="right"
render={(text, record: any, index) => {
return record.peer_groups ? (
<Tooltip
color="#fff"
overlayClassName="peer-avail-tooltip"
title={`Peer "${formPeer.name}" is a part of a group used in a network route.
To remove this peer from the network route, you need to disassociate
this peer from the groups used in this route.`}
>
<Button type={"text"} disabled={true}>
Delete
</Button>
</Tooltip>
) : (
<Button
danger={true}
type={"text"}
onClick={() => {
showConfirmDelete(
record.id,
record.network_id
);
}}
>
Delete
</Button>
);
}}
/>
</Table>
)}
<Divider style={{ marginTop: "-12px" }}></Divider>
{(peerRoutes === null || peerRoutes.length === 0) && (
<Space
direction="vertical"
size="small"
align="start"
style={{
display: "flex",
padding: "35px 0px",
marginTop: "-40px",
justifyContent: "center",
}}
>
<Paragraph
style={{ textAlign: "start", whiteSpace: "pre-line" }}
>
You don't have any routes yet
</Paragraph>
{peerRoutes && peerRoutes.length > 0 && (
<Button type="primary" onClick={onClickAddNewRoute}>
Add route
</Button>
</Space>
)}
</div>
</Card>
)}
)}
</Col>
</Row>
{peerRoutes && peerRoutes.length > 0 && (
<Table
size={"small"}
style={{ marginTop: "-10px" }}
showHeader={false}
scroll={{ x: 800 }}
pagination={false}
dataSource={peerRoutes}
>
<Column title="Name" dataIndex="network_id" />
<Column title="Name" dataIndex="network" />
<Column
title="enabled"
dataIndex="network"
render={(e, record: any, index) => {
return (
<>
<Switch
defaultChecked={record.enabled}
size="small"
onChange={(checked) =>
onRouteEnableChange(checked, record)
}
/>
</>
);
}}
/>
<Column
align="right"
render={(text, record: any, index) => {
return (
<Button
danger={true}
type={"text"}
onClick={() => {
showConfirmDelete(record.id, record.network_id);
}}
>
Delete
</Button>
);
}}
/>
</Table>
)}
<Divider style={{ marginTop: "-12px" }}></Divider>
{(peerRoutes === null || peerRoutes.length === 0) && (
<Space
direction="vertical"
size="small"
align="start"
style={{
display: "flex",
padding: "35px 0px",
marginTop: "-40px",
justifyContent: "center",
}}
>
<Paragraph
style={{ textAlign: "start", whiteSpace: "pre-line" }}
>
You don't have any routes yet
</Paragraph>
<Button type="primary" onClick={onClickAddNewRoute}>
Add route
</Button>
</Space>
)}
</div>
</Card>
<Card bordered={true} style={{ marginBottom: "50px" }}>
<Col span={24}>
+77 -164
View File
@@ -17,9 +17,7 @@ import {
Switch,
Modal,
Typography,
Tabs,
} from "antd";
import type { TabsProps } from "antd";
import CreatableSelect from "react-select/creatable";
import { Route, RouteToSave } from "../store/route/types";
import { Header } from "antd/es/layout/layout";
@@ -40,7 +38,6 @@ const { Panel } = Collapse;
interface FormRoute extends Route {}
const RouteAddNew = (selectedPeer: any) => {
const [activeTab, setActiveTab] = useState<string | null>(null);
const {
blueTagRender,
handleChangeTags,
@@ -78,10 +75,6 @@ const RouteAddNew = (selectedPeer: any) => {
const [peerNameToIP, peerIPToName, peerIPToID] = initPeerMaps(peers);
const [newRoute, setNewRoute] = useState(false);
useEffect(() => {
if (setupNewRouteVisible) setActiveTab("routingPeer");
}, [setupNewRouteVisible]);
useEffect(() => {
if (editName)
inputNameRef.current!.focus({
@@ -151,7 +144,6 @@ const RouteAddNew = (selectedPeer: any) => {
!os.toLowerCase().startsWith("darwin") &&
!os.toLowerCase().startsWith("windows") &&
!os.toLowerCase().startsWith("android") &&
!os.toLowerCase().startsWith("ios") &&
route &&
!routes
.filter((r) => r.network_id === route.network_id)
@@ -167,12 +159,6 @@ const RouteAddNew = (selectedPeer: any) => {
}
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
if (inputRoute.peer_groups) {
inputRoute = {
...inputRoute,
peer_groups: [inputRoute.peer_groups[inputRoute.peer_groups.length - 1]],
};
}
let peerIDList = inputRoute.peer.split(routePeerSeparator);
let peerID: string;
if (peerIDList.length === 1) {
@@ -189,70 +175,24 @@ const RouteAddNew = (selectedPeer: any) => {
inputRoute.groups
);
const payload = {
return {
id: inputRoute.id,
network: inputRoute.network,
network_id: inputRoute.network_id,
description: inputRoute.description,
peer: peerID,
enabled: inputRoute.enabled,
masquerade: inputRoute.masquerade,
metric: inputRoute.metric,
groups: existingGroups,
groupsToCreate: groupsToCreate,
} as RouteToSave;
if (activeTab === "routingPeer") {
let pay = { ...payload, peer: peerID };
return pay;
}
if (activeTab === "groupOfPeers") {
if (inputRoute.peer_groups) {
let [currentPeersGroup, peerGroupsToCreate] =
getExistingAndToCreateGroupsLists(inputRoute.peer_groups);
let pay = {
...payload,
peer_groups: currentPeersGroup,
peerGroupsToCreate: peerGroupsToCreate,
};
return pay;
}
}
return payload;
};
const handleFormSubmit = () => {
form
.validateFields()
.then(() => {
const t = routes.filter((route) => {
if (
route.network_id === formRoute.network_id &&
route.network === formRoute.network
) {
return route;
}
});
if (
formRoute.peer_groups &&
formRoute.peer_groups.length > 0 &&
t &&
t.length > 0
) {
const style = { marginTop: 85 };
const duplicateNetworkIdKey = "duplicateKey";
return message.error({
content:
"A route with this network identifier and network range already exists. Please use a different network identifier or network range.",
key: duplicateNetworkIdKey,
duration: 5,
style,
});
}
if (!setupNewRouteHA || formRoute.peer != "") {
const routeToSave = createRouteToSave(formRoute);
dispatch(
@@ -303,7 +243,6 @@ const RouteAddNew = (selectedPeer: any) => {
const onCancel = () => {
if (savedRoute.loading) return;
setActiveTab(null);
setEditName(false);
dispatch(
routeActions.setRoute({
@@ -315,7 +254,6 @@ const RouteAddNew = (selectedPeer: any) => {
masquerade: false,
enabled: true,
groups: [],
peer_groups: [],
} as Route)
);
setVisibleNewRoute(false);
@@ -362,15 +300,8 @@ const RouteAddNew = (selectedPeer: any) => {
return Promise.resolve();
};
const peerGroupsValidaton = (_: RuleObject, value: string) => {
if (value.length < 1) {
return Promise.reject(new Error("Please select a peer group"));
}
return Promise.resolve();
};
const selectPreValidator = (obj: RuleObject, value: string[]) => {
if (setupNewRouteHA && formRoute.peer === "") {
if (setupNewRouteHA && formRoute.peer == "") {
let [, newGroups] = getExistingAndToCreateGroupsLists(value);
if (newGroups.length > 0) {
return Promise.reject(
@@ -430,95 +361,56 @@ const RouteAddNew = (selectedPeer: any) => {
}
};
const onTabChange = (key: string) => {
setActiveTab(key);
};
const styleNotification = { marginTop: 85 };
const handleSingleChangeTags = (values: any) => {
const lastValue = values[values.length - 1];
if (values.length > 0) {
form.setFieldsValue({
peer_groups: [lastValue],
const saveKey = "saving";
useEffect(() => {
if (savedRoute.loading) {
message.loading({
content: "Saving...",
key: saveKey,
duration: 0,
style: styleNotification,
});
} else if (savedRoute.success) {
message.success({
content: "Route has been successfully added.",
key: saveKey,
duration: 2,
style: styleNotification,
});
dispatch(routeActions.setSetupNewRouteVisible(false));
dispatch(routeActions.setSetupEditRouteVisible(false));
dispatch(routeActions.setSetupEditRoutePeerVisible(false));
dispatch(routeActions.setSavedRoute({ ...savedRoute, success: false }));
dispatch(routeActions.resetSavedRoute(null));
} else if (savedRoute.error) {
let errorMsg = "Failed to update network route";
switch (savedRoute.error.statusCode) {
case 403:
errorMsg =
"Failed to update network route. You might not have enough permissions.";
break;
default:
errorMsg = savedRoute.error.data.message
? savedRoute.error.data.message
: errorMsg;
break;
}
message.error({
content: errorMsg,
key: saveKey,
duration: 5,
style: styleNotification,
});
dispatch(routeActions.setSavedRoute({ ...savedRoute, error: null }));
dispatch(routeActions.resetSavedRoute(null));
}
};
const items: TabsProps["items"] = [
{
key: "routingPeer",
label: "Routing Peer",
children: (
<>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
{activeTab === "routingPeer" && (
<Form.Item name="peer" rules={[{ validator: peerValidator }]}>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
disabled={!!selectedPeer.selectedPeer}
/>
</Form.Item>
)}{" "}
</>
),
},
{
key: "groupOfPeers",
label: "Peer group",
children: (
<>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign peer group with Linux machines to be used as routing peers
</Paragraph>
{activeTab === "groupOfPeers" && (
<Form.Item
name="peer_groups"
rules={[{ validator: peerGroupsValidaton }]}
>
<Select
mode="tags"
style={{ width: "100%" }}
tagRender={blueTagRender}
onChange={handleSingleChangeTags}
dropdownRender={dropDownRender}
optionFilterProp="serchValue"
>
{tagGroups.map((m, index) => (
<Option key={index} value={m.id} serchValue={m.name}>
{optionRender(m.name, m.id)}
</Option>
))}
</Select>
</Form.Item>
)}
</>
),
},
];
}, [savedRoute]);
return (
<>
{route && setupNewRouteVisible && (
{route && (
<Modal
open={setupNewRouteVisible}
onCancel={onCancel}
@@ -666,7 +558,7 @@ const RouteAddNew = (selectedPeer: any) => {
marginBottom: "5px",
}}
>
Add a unique network identifier that is assigned
Add a unique cryptographic key that is assigned
to each device
</Paragraph>
<Form.Item
@@ -775,15 +667,36 @@ const RouteAddNew = (selectedPeer: any) => {
{!!!selectedPeer.selectedPeer && (
<Col span={24}>
{activeTab ? (
<Tabs
defaultActiveKey={activeTab}
items={items}
onChange={onTabChange}
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "500",
}}
>
Routing Peer
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
<Form.Item name="peer" rules={[{ validator: peerValidator }]}>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
disabled={!!selectedPeer.selectedPeer}
/>
) : (
""
)}
</Form.Item>
</Col>
)}
<Col span={24}>
+34 -150
View File
@@ -17,13 +17,10 @@ import {
Switch,
Card,
Typography,
Tabs,
} from "antd";
import type { TabsProps } from "antd";
import { Route, RouteToSave } from "../store/route/types";
import { Header } from "antd/es/layout/layout";
import { RuleObject } from "antd/lib/form";
import { isEmpty } from "lodash";
import {
initPeerMaps,
peerToPeerIP,
@@ -64,16 +61,6 @@ const RoutePeerUpdate = () => {
const [form] = Form.useForm();
const inputDescriptionRef = useRef<any>(null);
const [peerNameToIP, peerIPToName, peerIPToID] = initPeerMaps(peers);
const [activeTab, setActiveTab] = useState<string | null>(null);
useEffect(() => {
if (!isEmpty(formRoute))
if (formRoute.peer_groups) {
setActiveTab("groupOfPeers");
} else {
setActiveTab("routingPeer");
}
}, [formRoute]);
useEffect(() => {
if (editDescription)
@@ -100,7 +87,6 @@ const RoutePeerUpdate = () => {
!os.toLowerCase().startsWith("darwin") &&
!os.toLowerCase().startsWith("windows") &&
!os.toLowerCase().startsWith("android") &&
!os.toLowerCase().startsWith("ios") &&
route &&
!routes
.filter((r) => r.network_id === route.network_id)
@@ -114,25 +100,7 @@ const RoutePeerUpdate = () => {
}
});
const handleSingleChangeTags = (values: any) => {
const lastValue = values[values.length - 1];
if (values.length > 0) {
form.setFieldsValue({
peer_groups: [lastValue],
});
}
};
const createRouteToSave = (inputRoute: FormRoute): RouteToSave => {
if (inputRoute.peer_groups) {
inputRoute = {
...inputRoute,
peer_groups: [
inputRoute.peer_groups[inputRoute.peer_groups.length - 1],
],
};
}
let peerIDList = inputRoute.peer.split(routePeerSeparator);
let peerID: string;
if (peerIDList.length === 1) {
@@ -148,38 +116,18 @@ const RoutePeerUpdate = () => {
inputRoute.groups
);
const payload = {
return {
id: inputRoute.id,
network: inputRoute.network,
network_id: inputRoute.network_id,
description: inputRoute.description,
peer: peerID,
enabled: inputRoute.enabled,
masquerade: inputRoute.masquerade,
metric: inputRoute.metric,
groups: existingGroups,
groupsToCreate: groupsToCreate,
} as RouteToSave;
if (activeTab === "routingPeer") {
let pay = { ...payload, peer: peerID };
return pay;
}
if (activeTab === "groupOfPeers") {
if (inputRoute.peer_groups) {
let [currentPeersGroup, peerGroupsToCreate] =
getExistingAndToCreateGroupsLists(inputRoute.peer_groups);
let pay = {
...payload,
peer_groups: currentPeersGroup,
peerGroupsToCreate: peerGroupsToCreate,
};
return pay;
}
}
return payload;
};
const handleFormSubmit = () => {
@@ -217,7 +165,6 @@ const RoutePeerUpdate = () => {
masquerade: false,
enabled: true,
groups: [],
peer_groups: [],
} as Route)
);
setVisibleNewRoute(false);
@@ -322,95 +269,6 @@ const RoutePeerUpdate = () => {
}
}, [savedRoute]);
const onTabChange = (key: string) => {
console.log(key);
// setActiveTab(key);
};
const items: TabsProps["items"] = [
{
key: "routingPeer",
label: "Routing Peer",
disabled: true,
children: (
<>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
<Form.Item
name="peer"
rules={[
{
required: true,
message: "Please select routing one peer",
},
]}
style={{ maxWidth: "400px" }}
>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
/>
</Form.Item>
</>
),
},
{
key: "groupOfPeers",
label: "Peer group",
disabled: true,
children: (
<>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign peer group with Linux machines to be used as routing peers
</Paragraph>
<Form.Item
name="peer_groups"
rules={[
{
required: true,
message: "Please select peer group",
},
]}
>
<Select
mode="tags"
style={{ maxWidth: "400px" }}
tagRender={blueTagRender}
onChange={handleSingleChangeTags}
dropdownRender={dropDownRender}
optionFilterProp="serchValue"
>
{tagGroups.map((m, index) => (
<Option key={index} value={m.id} serchValue={m.name}>
{optionRender(m.name, m.id)}
</Option>
))}
</Select>
</Form.Item>
</>
),
},
];
return (
<>
<Container style={{ paddingTop: "40px", paddingBottom: "50px" }}>
@@ -551,13 +409,39 @@ const RoutePeerUpdate = () => {
</Col>
<Col span={24}>
{activeTab && (
<Tabs
defaultActiveKey={activeTab}
items={items}
onChange={onTabChange}
<label
style={{
color: "rgba(0, 0, 0, 0.88)",
fontSize: "14px",
fontWeight: "500",
}}
>
Routing Peer
</label>
<Paragraph
type={"secondary"}
style={{
marginTop: "-2",
fontWeight: "400",
marginBottom: "5px",
}}
>
Assign a peer as a routing peer for the Network CIDR
</Paragraph>
<Form.Item
name="peer"
rules={[{ validator: peerValidator }]}
style={{ maxWidth: "400px" }}
>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Select Peer"
dropdownRender={peerDropDownRender}
options={options}
allowClear={true}
/>
)}
</Form.Item>
</Col>
<Col span={24}>
<label
Loaded 20 of 54 files, more files were not shown because too many files have changed in this diff. Show more