diff --git a/src/components/PeerUpdate.tsx b/src/components/PeerUpdate.tsx
index 8522a9b..372d90b 100644
--- a/src/components/PeerUpdate.tsx
+++ b/src/components/PeerUpdate.tsx
@@ -295,7 +295,7 @@ const PeerUpdate = () => {
) : (
{
+ return formatDate(value)
+}
+
+const customLastUsedFormat: DatePickerProps['format'] = value => {
+ if (value.toString().startsWith("0001")) {
+ return "never"
+ }
+ let ago = timeAgo(value.toString())
+ if (!ago) {
+ return "unused"
+ }
+ return ago
+}
+
+interface FormSetupKey extends SetupKey {
+ autoGroupNames: string[]
+}
const SetupKeyNew = () => {
const {accessToken} = useOidcAccessToken()
const dispatch = useDispatch()
const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible)
- const setupKey = useSelector((state: RootState) => state.setupKey.setupKey)
- const createdSetupKey = useSelector((state: RootState) => state.setupKey.createdSetupKey)
+ const setupKey = useSelector((state: RootState) => state.setupKey.setupKey)
+ const savedSetupKey = useSelector((state: RootState) => state.setupKey.savedSetupKey)
+ const groups = useSelector((state: RootState) => state.group.data)
+ const [editName, setEditName] = useState(false)
+ const inputNameRef = useRef(null)
+ const [selectedTagGroups, setSelectedTagGroups] = useState([] as string[])
+ const [tagGroups, setTagGroups] = useState([] as string[])
- const [formSetupKey, setFormSetupKey] = useState({} as SetupKey)
+ const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey)
const [form] = Form.useForm()
useEffect(() => {
- setFormSetupKey({ ...setupKey } as SetupKey)
- form.setFieldsValue(setupKey)
+ if (editName) inputNameRef.current!.focus({
+ cursor: 'end',
+ });
+ }, [editName]);
+
+ useEffect(() => {
+ setTagGroups(groups?.filter(g => g.name != "All").map(g => g.name) || [])
+ }, [groups])
+
+ useEffect(() => {
+ if (!setupKey) return
+
+ let allGroups = new Map();
+ groups.forEach(g => {
+ allGroups.set(g.id!, g)
+ })
+
+ let formKeyGroups :string[] = []
+
+ if (setupKey.auto_groups) {
+ formKeyGroups = setupKey.auto_groups.filter(g => allGroups.get(g)).map(g => allGroups.get(g)!.name)
+ }
+
+ const fSetupKey = {
+ ...setupKey,
+ autoGroupNames: setupKey.auto_groups ? formKeyGroups : [],
+ } as FormSetupKey
+ setFormSetupKey(fSetupKey)
+ form.setFieldsValue(fSetupKey)
}, [setupKey])
+ const createSetupKeyToSave = (): SetupKeyToSave => {
+ const autoGroups = groups?.filter(g => formSetupKey.autoGroupNames.includes(g.name)).map(g => g.id || '') || []
+ // find groups that do not yet exist (newly added by the user)
+ const allGroupsNames : string[] = groups?.map(g => g.name);
+ const groupsToCreate = formSetupKey.autoGroupNames.filter(s => !allGroupsNames.includes(s))
+ return {
+ id: formSetupKey.id,
+ name: formSetupKey.name,
+ type: formSetupKey.type,
+ auto_groups: autoGroups,
+ revoked: formSetupKey.revoked,
+ groupsToCreate: groupsToCreate
+ } as SetupKeyToSave
+ }
const handleFormSubmit = () => {
form.validateFields()
.then((values) => {
- dispatch(setupKeyActions.createSetupKey.request({getAccessTokenSilently:accessToken, payload: formSetupKey}))
+ let setupKeyToSave = createSetupKeyToSave()
+ dispatch(setupKeyActions.saveSetupKey.request({
+ getAccessTokenSilently: accessToken,
+ payload: setupKeyToSave
+ }))
})
.catch((errorInfo) => {
console.log('errorInfo', errorInfo)
});
};
-
- const setVisibleNewSetupKey = (status:boolean) => {
+
+ const setVisibleNewSetupKey = (status: boolean) => {
dispatch(setupKeyActions.setSetupNewKeyVisible(status));
}
const onCancel = () => {
- if (createdSetupKey.loading) return
+ if (savedSetupKey.loading) return
dispatch(setupKeyActions.setSetupKey({
- name: '',
- type: 'reusable'
+ name: "",
+ type: "reusable",
+ key: "",
+ last_used: "",
+ expires: "",
+ state: "valid",
+ auto_groups: new Array()
} as SetupKey))
+ setFormSetupKey({} as FormSetupKey)
setVisibleNewSetupKey(false)
}
- const onChange = (data:any) => {
+ const onChange = (data: any) => {
setFormSetupKey({...formSetupKey, ...data})
}
+ const toggleEditName = (status: boolean) => {
+ setEditName(status);
+ }
+
+ const selectValidator = (_: RuleObject, value: string[]) => {
+ let hasSpaceNamed = []
+
+ value.forEach(function (v: string) {
+ if (!v.trim().length) {
+ hasSpaceNamed.push(v)
+ }
+ })
+
+ if (hasSpaceNamed.length) {
+ return Promise.reject(new Error("Group names with just spaces are not allowed"))
+ }
+
+ return Promise.resolve()
+ }
+
+ const tagRender = (props: CustomTagProps) => {
+ const {label, value, closable, onClose} = props;
+ const onPreventMouseDown = (event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ return (
+
+ {value}
+
+ );
+ }
+
+ const optionRender = (label: string) => {
+ let peersCount = ''
+ const g = groups.find(_g => _g.name === label)
+ if (g) peersCount = ` - ${g.peers_count || 0} ${(!g.peers_count || parseInt(g.peers_count) !== 1) ? 'peers' : 'peer'} `
+ return (
+ <>
+
+ {label}
+
+ {peersCount}
+ >
+ )
+ }
+
+ const dropDownRender = (menu: React.ReactElement) => (
+ <>
+ {menu}
+
+
+
+ Add new group by pressing "Enter"
+
+
+
+
+
+
+
+ >
+ )
+
+ const handleChangeTags = (value: string[]) => {
+ let validatedValues: string[] = []
+ value.forEach(function (v) {
+ if (v.trim().length) {
+ validatedValues.push(v)
+ }
+ })
+ setSelectedTagGroups(validatedValues)
+ };
+
+ const inputLabel = (text: any) => (
+ <>
+ {text}
+ {formSetupKey.state}
+ >
+ )
return (
<>
- {setupKey &&
-
- Cancel
- Create
-
- }
- >
-
+ {setupKey.id && formSetupKey.name &&
+
+
+
+
+
+ }
+ {setupKey.id && formSetupKey.name &&
+
+
+
+
+
+ }
+
+
+
+
+
+
+
+
+ Reusable
+ This type of a setup key allows to enroll multiple
+ machines
+
+
+
+
+
+
+ One-off
+ This key can be used only once
+
+
+
+
-
- }
+
+
+
+
+
+
+
+
+ {
+ tagGroups.map(m =>
+ {optionRender(m)}
+ )
+ }
+
+
+
+
+
+ } type="link" target="_blank"
+ href="https://netbird.io/docs/overview/setup-keys"
+ style={{color: 'rgb(07, 114, 128)'}}>Learn
+ more about setup keys
+
+
+
+
+
+ }
>
)
}
diff --git a/src/store/rule/sagas.ts b/src/store/rule/sagas.ts
index 08a33a3..d632b56 100644
--- a/src/store/rule/sagas.ts
+++ b/src/store/rule/sagas.ts
@@ -53,7 +53,6 @@ export function* saveRule(action: ReturnType):
})
))
-
const resGroups = (responsesGroup as ApiResponse[]).filter(r => r.statusCode === 200).map(r => (r.body as Group))
const currentGroups = [...(yield select(state => state.group.data)) as Rule[]]
diff --git a/src/store/setup-key/actions.ts b/src/store/setup-key/actions.ts
index 1caedf0..214159e 100644
--- a/src/store/setup-key/actions.ts
+++ b/src/store/setup-key/actions.ts
@@ -1,8 +1,7 @@
import { ActionType, createAction, createAsyncAction } from 'typesafe-actions';
-import {SetupKey, SetupKeyRevoke} from './types';
+import {SetupKey, SetupKeyToSave} from './types';
import {
ApiError,
- ChangeResponse,
CreateResponse,
DeleteResponse,
RequestPayload
@@ -15,12 +14,13 @@ const actions = {
'GET_SETUP_KEYS_FAILURE',
), SetupKey[], ApiError>(),
- createSetupKey: createAsyncAction(
- 'CREATE_SETUP_KEY_REQUEST',
- 'CREATE_SETUP_KEY_SUCCESS',
- 'CREATE_SETUP_KEY_FAILURE',
- ), CreateResponse, CreateResponse>(),
- setCreateSetupKey: createAction('SET_CREATE_SETUP_KEY')>(),
+ saveSetupKey: createAsyncAction(
+ 'SAVE_SETUP_KEY_REQUEST',
+ 'SAVE_SETUP_KEY_SUCCESS',
+ 'SAVE_SETUP_KEY_FAILURE',
+ ), CreateResponse, CreateResponse>(),
+ setSavedSetupKey: createAction('SET_SAVE_SETUP_KEY')>(),
+ resetSavedSetupKey: createAction('RESET_SAVE_SETUP_KEY')(),
deleteSetupKey: createAsyncAction(
'DELETE_SETUP_KEY_REQUEST',
@@ -30,15 +30,6 @@ const actions = {
setDeleteSetupKey: createAction('SET_DELETE_SETUP_KEY')>(),
resetDeletedSetupKey: createAction('RESET_DELETE_SETUP_KEY')(),
- revokeSetupKey: createAsyncAction(
- 'REVOKE_SETUP_KEY_REQUEST',
- 'REVOKE_SETUP_KEY_SUCCESS',
- 'REVOKE_SETUP_KEY_FAILURE'
- ), ChangeResponse, ChangeResponse>(),
- setRevokeSetupKey: createAction('SET_REVOKED_SETUP_KEY')>(),
- resetRevokedSetupKey: createAction('RESET_REVOKED_SETUP_KEY')(),
-
-
removeSetupKey: createAction('REMOVE_SETUP_KEY')(),
setSetupKey: createAction('SET_SETUP_KEY')(),
setSetupNewKeyVisible: createAction('SET_SETUP_NEW_KEY_VISIBLE')()
diff --git a/src/store/setup-key/reducer.ts b/src/store/setup-key/reducer.ts
index a45f03f..1dcf35f 100644
--- a/src/store/setup-key/reducer.ts
+++ b/src/store/setup-key/reducer.ts
@@ -12,7 +12,7 @@ type StateType = Readonly<{
saving: boolean;
deletedSetupKey: DeleteResponse;
revokedSetupKey: ChangeResponse;
- createdSetupKey: CreateResponse;
+ savedSetupKey: CreateResponse;
setupNewKeyVisible: boolean
}>;
@@ -36,7 +36,7 @@ const initialState: StateType = {
error: null,
data : null
},
- createdSetupKey: >{
+ savedSetupKey: >{
loading: false,
success: false,
failure: false,
@@ -75,18 +75,12 @@ const deletedSetupKey = createReducer, ActionTypes
.handleAction(actions.setDeleteSetupKey, (store, action) => action.payload)
.handleAction(actions.resetDeletedSetupKey, (store, action) => initialState.deletedSetupKey);
-const revokedSetupKey = createReducer, ActionTypes>(initialState.revokedSetupKey)
- .handleAction(actions.revokeSetupKey.request, () => initialState.revokedSetupKey)
- .handleAction(actions.revokeSetupKey.success, (store, action) => action.payload)
- .handleAction(actions.revokeSetupKey.failure, (store, action) => action.payload)
- .handleAction(actions.setRevokeSetupKey, (store, action) => action.payload)
- .handleAction(actions.resetRevokedSetupKey, () => initialState.revokedSetupKey)
-
-const createdSetupKey = createReducer, ActionTypes>(initialState.createdSetupKey)
- .handleAction(actions.createSetupKey.request, () => initialState.createdSetupKey)
- .handleAction(actions.createSetupKey.success, (store, action) => action.payload)
- .handleAction(actions.createSetupKey.failure, (store, action) => action.payload)
- .handleAction(actions.setCreateSetupKey, (store, action) => action.payload)
+const savedSetupKey = createReducer, ActionTypes>(initialState.savedSetupKey)
+ .handleAction(actions.saveSetupKey.request, () => initialState.savedSetupKey)
+ .handleAction(actions.saveSetupKey.success, (store, action) => action.payload)
+ .handleAction(actions.saveSetupKey.failure, (store, action) => action.payload)
+ .handleAction(actions.setSavedSetupKey, (store, action) => action.payload)
+ .handleAction(actions.resetSavedSetupKey, () => initialState.savedSetupKey)
const setupNewKeyVisible = createReducer(initialState.setupNewKeyVisible)
.handleAction(actions.setSetupNewKeyVisible, (store, action) => action.payload)
@@ -98,7 +92,6 @@ export default combineReducers({
failed,
saving,
deletedSetupKey,
- revokedSetupKey,
- createdSetupKey,
+ savedSetupKey: savedSetupKey,
setupNewKeyVisible
});
diff --git a/src/store/setup-key/sagas.ts b/src/store/setup-key/sagas.ts
index 0086b1e..99b339f 100644
--- a/src/store/setup-key/sagas.ts
+++ b/src/store/setup-key/sagas.ts
@@ -1,27 +1,36 @@
import {all, call, put, select, takeLatest} from 'redux-saga/effects';
-import {ApiError, ApiResponse, ChangeResponse, CreateResponse, DeleteResponse} from '../../services/api-client/types';
-import {SetupKey, SetupKeyRevoke} from './types'
+import {ApiError, ApiResponse, CreateResponse, DeleteResponse} from '../../services/api-client/types';
+import {SetupKey, SetupKeyToSave} from './types'
import service from './service';
import actions from './actions';
+import serviceGroup from "../group/service";
+import {Group} from "../group/types";
+import {actions as groupActions} from "../group";
export function* getSetupKeys(action: ReturnType): Generator {
try {
const effect = yield call(service.getSetupKeys, action.payload);
const response = effect as ApiResponse;
- yield put(actions.getSetupKeys.success(response.body));
+ yield put(actions.getSetupKeys.success(response.body.map(k => {
+ // always set auto_groups even if absent (avoid null)
+ if (k.auto_groups) {
+ return k
+ }
+ return {...k, auto_groups: []}
+ })));
} catch (err) {
yield put(actions.getSetupKeys.failure(err as ApiError));
}
}
-export function* setCreateSetupKey(action: ReturnType): Generator {
- yield put(actions.setCreateSetupKey(action.payload))
+export function* setCreateSetupKey(action: ReturnType): Generator {
+ yield put(actions.setSavedSetupKey(action.payload))
}
-export function* createSetupKey(action: ReturnType): Generator {
+export function* saveSetupKey(action: ReturnType): Generator {
try {
- yield put(actions.setCreateSetupKey({
+ yield put(actions.setSavedSetupKey({
loading: true,
success: false,
failure: false,
@@ -29,10 +38,46 @@ export function* createSetupKey(action: ReturnType))
- const effect = yield call(service.createSetupKey, action.payload);
+ const keyToSave = action.payload.payload
+
+ let groupsToCreate = keyToSave.groupsToCreate
+ if (!groupsToCreate) {
+ groupsToCreate = []
+ }
+
+ // first, create groups that were newly added by user
+ const responsesGroup = yield all(groupsToCreate.map(g => call(serviceGroup.createGroup, {
+ getAccessTokenSilently: action.payload.getAccessTokenSilently,
+ payload: { name: g }
+ })
+ ))
+
+ const resGroups = (responsesGroup as ApiResponse[]).filter(r => r.statusCode === 200).map(g => (g.body as Group)).map(g => g.id)
+ const newGroups = [...keyToSave.auto_groups, ...resGroups]
+ let effect
+ if (!keyToSave.id) {
+ effect = yield call(service.createSetupKey, {
+ getAccessTokenSilently: action.payload.getAccessTokenSilently,
+ payload: {
+ name: keyToSave.name,
+ auto_groups: newGroups,
+ type: keyToSave.type
+ } as SetupKeyToSave
+ });
+ } else {
+ effect = yield call(service.editSetupKey, {
+ getAccessTokenSilently: action.payload.getAccessTokenSilently,
+ payload: {
+ id: keyToSave.id,
+ name: keyToSave.name,
+ revoked: keyToSave.revoked,
+ auto_groups: newGroups,
+ } as SetupKeyToSave
+ });
+ }
const response = effect as ApiResponse;
- yield put(actions.createSetupKey.success({
+ yield put(actions.saveSetupKey.success({
loading: false,
success: true,
failure: false,
@@ -40,11 +85,10 @@ export function* createSetupKey(action: ReturnType));
- const setupKeys = [...(yield select(state => state.setupKey.data)) as SetupKey[]]
- setupKeys.unshift(response.body)
- yield put(actions.getSetupKeys.success(setupKeys));
+ yield put(groupActions.getGroups.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null }));
+ yield put(actions.getSetupKeys.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null }));
} catch (err) {
- yield put(actions.createSetupKey.failure({
+ yield put(actions.saveSetupKey.failure({
loading: false,
success: false,
failure: false,
@@ -92,53 +136,11 @@ export function* deleteSetupKey(action: ReturnType): Generator {
- try {
- yield put(actions.setRevokeSetupKey({
- loading: true,
- success: false,
- failure: false,
- error: null,
- data: null
- } as ChangeResponse))
-
- const effect = yield call(service.revokeSetupKey, action.payload);
- const response = effect as ApiResponse;
-
- yield put(actions.revokeSetupKey.success({
- loading: false,
- success: true,
- failure: false,
- error: null,
- data: response.body
- } as ChangeResponse));
-
- const setupKeys = [...(yield select(state => state.setupKey.data)) as SetupKey[]]
- let setupKey = setupKeys.find(s => s.id === response.body.id) as SetupKey
- if (setupKey) {
- setupKey.revoked = response.body.revoked
- setupKey.valid = response.body.valid
- setupKey.state = response.body.state
- setupKey.expires = response.body.expires
- }
- yield put(actions.getSetupKeys.success(setupKeys));
- } catch (err) {
- yield put(actions.createSetupKey.failure({
- loading: false,
- success: false,
- failure: false,
- error: err as ApiError,
- data: null
- } as CreateResponse));
- }
-}
-
export default function* sagas(): Generator {
yield all([
takeLatest(actions.getSetupKeys.request, getSetupKeys),
- takeLatest(actions.createSetupKey.request, createSetupKey),
- takeLatest(actions.deleteSetupKey.request, deleteSetupKey),
- takeLatest(actions.revokeSetupKey.request, revokeSetupKey)
+ takeLatest(actions.saveSetupKey.request, saveSetupKey),
+ takeLatest(actions.deleteSetupKey.request, deleteSetupKey)
]);
}
diff --git a/src/store/setup-key/service.ts b/src/store/setup-key/service.ts
index 6b40715..a6fb0e7 100644
--- a/src/store/setup-key/service.ts
+++ b/src/store/setup-key/service.ts
@@ -1,6 +1,6 @@
import {ApiResponse, RequestPayload} from '../../services/api-client/types';
import { apiClient } from '../../services/api-client';
-import {SetupKey, SetupKeyNew, SetupKeyRevoke} from './types';
+import {SetupKey, SetupKeyToSave} from './types';
export default {
async getSetupKeys(payload:RequestPayload): Promise> {
@@ -15,22 +15,19 @@ export default {
payload
);
},
- async revokeSetupKey(payload:RequestPayload): Promise> {
- return apiClient.put(
- `/api/setup-keys/` + payload.payload.id,
- payload
- );
- },
- async renameSetupKey(payload:RequestPayload): Promise> {
- return apiClient.put(
- `/api/setup-keys/` + payload.payload.id,
- payload
- );
- },
- async createSetupKey(payload:RequestPayload): Promise> {
+ async createSetupKey(payload:RequestPayload): Promise> {
return apiClient.post(
`/api/setup-keys`,
payload
);
},
+ async editSetupKey(payload:RequestPayload): Promise> {
+ const id = payload.payload.id
+ // @ts-ignore
+ delete payload.payload.id
+ return apiClient.put(
+ `/api/setup-keys/${id}`,
+ payload
+ );
+ },
};
diff --git a/src/store/setup-key/types.ts b/src/store/setup-key/types.ts
index 29eb320..476b7b9 100644
--- a/src/store/setup-key/types.ts
+++ b/src/store/setup-key/types.ts
@@ -1,3 +1,5 @@
+import {Group} from "../group/types";
+
export interface SetupKey {
expires: string;
id: string;
@@ -9,15 +11,10 @@ export interface SetupKey {
type: string;
used_times: number;
valid: boolean;
+ auto_groups: string[]
}
-export interface SetupKeyNew {
- id: string;
- name: string;
- type: string;
-}
-
-export interface SetupKeyRevoke {
- id: string;
- revoked: boolean;
+export interface SetupKeyToSave extends SetupKey
+{
+ groupsToCreate: string[]
}
diff --git a/src/utils/routes.ts b/src/utils/routes.ts
index cf79ae9..6355b15 100644
--- a/src/utils/routes.ts
+++ b/src/utils/routes.ts
@@ -66,7 +66,6 @@ export const transformGroupedDataTable = (routes:Route[],peerIPToName:PeerIPToNa
}
})
let groupDataTableRoutes = transformDataTable(listedRoutes,peerIPToName)
- console.log(groupDataTableRoutes.length)
groupedRoutes.push({
key: p.toString(),
network_id: lastRoute!.network_id,
@@ -78,6 +77,5 @@ export const transformGroupedDataTable = (routes:Route[],peerIPToName:PeerIPToNa
groupedRoutes: groupDataTableRoutes,
})
})
- console.log(groupedRoutes)
return groupedRoutes
}
\ No newline at end of file
diff --git a/src/views/AccessControl.tsx b/src/views/AccessControl.tsx
index 2315083..8f8e57c 100644
--- a/src/views/AccessControl.tsx
+++ b/src/views/AccessControl.tsx
@@ -126,7 +126,7 @@ export const AccessControl = () => {
if (savedRule.loading) {
message.loading({ content: 'Saving...', key: saveKey, duration: 0, style: styleNotification })
} else if (savedRule.success) {
- message.success({ content: 'Rule has been successfully updated.', key: saveKey, duration: 2, style: styleNotification });
+ message.success({ content: 'Rule has been successfully saved.', key: saveKey, duration: 2, style: styleNotification });
dispatch(ruleActions.setSetupNewRuleVisible(false))
dispatch(ruleActions.setSavedRule({ ...savedRule, success: false }))
dispatch(ruleActions.resetSavedRule(null))
diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx
index ae73a58..6773f4b 100644
--- a/src/views/Peers.tsx
+++ b/src/views/Peers.tsx
@@ -67,7 +67,7 @@ export const Peers = () => {
const [textToSearch, setTextToSearch] = useState('');
const [optionOnOff, setOptionOnOff] = useState('all');
- const [pageSize, setPageSize] = useState(5);
+ const [pageSize, setPageSize] = useState(10);
const [dataTable, setDataTable] = useState([] as PeerDataTable[]);
const [peerToAction, setPeerToAction] = useState(null as PeerDataTable | null);
@@ -82,7 +82,7 @@ export const Peers = () => {
const itemsMenuAction = [
{
key: "view",
- label: ( onClickViewRule()}>View )
+ label: ( onClickViewPeer()}>View )
},
{
key: "delete",
@@ -300,7 +300,7 @@ export const Peers = () => {
}
- const onClickViewRule = () => {
+ const onClickViewPeer = () => {
dispatch(peerActions.setUpdateGroupsVisible(true))
dispatch(peerActions.setPeer(peerToAction as Peer))
}
diff --git a/src/views/SetupKeys.tsx b/src/views/SetupKeys.tsx
index 61f2d60..5550175 100644
--- a/src/views/SetupKeys.tsx
+++ b/src/views/SetupKeys.tsx
@@ -1,38 +1,46 @@
import React, {useEffect, useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
-import { RootState } from "typesafe-actions";
-import { actions as setupKeyActions } from '../store/setup-key';
+import {RootState} from "typesafe-actions";
+import {actions as setupKeyActions} from '../store/setup-key';
import {Container} from "../components/Container";
import {useOidcAccessToken} from '@axa-fr/react-oidc';
import {
- Col,
- Row,
- Typography,
- Table,
+ Alert,
+ Button,
Card,
- Tag,
+ Col,
+ Dropdown,
Input,
- Space,
+ Menu,
+ message,
+ Modal, Popover,
Radio,
RadioChangeEvent,
- Dropdown,
- Menu,
- Alert, Select, Modal, Button, message, Drawer, Form, List
+ Row,
+ Select,
+ Space,
+ Table,
+ Tag,
+ Typography
} from "antd";
-import {SetupKey, SetupKeyRevoke} from "../store/setup-key/types";
+import {SetupKey, SetupKeyToSave} from "../store/setup-key/types";
import {filter} from "lodash"
import {formatDate, timeAgo} from "../utils/common";
import {ExclamationCircleOutlined} from "@ant-design/icons";
import SetupKeyNew from "../components/SetupKeyNew";
import ButtonCopyMessage from "../components/ButtonCopyMessage";
import tableSpin from "../components/Spin";
+import {actions as groupActions} from "../store/group";
+import {Group} from "../store/group/types";
+import {TooltipPlacement} from "antd/es/tooltip";
-const { Title, Text, Paragraph } = Typography;
-const { Column } = Table;
-const { confirm } = Modal;
+const {Title, Text, Paragraph} = Typography;
+const {Column} = Table;
+const {confirm} = Modal;
interface SetupKeyDataTable extends SetupKey {
key: string
+ groupsCount: number
}
export const SetupKeys = () => {
@@ -43,16 +51,16 @@ export const SetupKeys = () => {
const failed = useSelector((state: RootState) => state.setupKey.failed);
const loading = useSelector((state: RootState) => state.setupKey.loading);
const deletedSetupKey = useSelector((state: RootState) => state.setupKey.deletedSetupKey);
- const revokedSetupKey = useSelector((state: RootState) => state.setupKey.revokedSetupKey);
- const createdSetupKey = useSelector((state: RootState) => state.setupKey.createdSetupKey);
+ const savedSetupKey = useSelector((state: RootState) => state.setupKey.savedSetupKey);
+ const groups = useSelector((state: RootState) => state.group.data)
const [textToSearch, setTextToSearch] = useState('');
const [optionValidAll, setOptionValidAll] = useState('valid');
- const [pageSize, setPageSize] = useState(5);
+ const [pageSize, setPageSize] = useState(10);
const [dataTable, setDataTable] = useState([] as SetupKeyDataTable[]);
const [setupKeyToAction, setSetupKeyToAction] = useState(null as SetupKeyDataTable | null);
- const styleNotification = { marginTop: 85 }
+ const styleNotification = {marginTop: 85}
const pageSizeOptions = [
{label: "5", value: "5"},
@@ -67,19 +75,21 @@ export const SetupKeys = () => {
key: "revoke",
label: ( showConfirmRevoke()}>Revoke )
},
- /*{
- key: "delete",
- label: ( showConfirmDelete()}>Delete )
- }*/
- ]
- const actionsMenu = ( )
+ {
+ key: "edit",
+ label: ( onClickEditSetupKey()}>View )
+ },
- const transformDataTable = (d:SetupKey[]):SetupKeyDataTable[] => {
- return d.map(p => ({ ...p } as SetupKeyDataTable))
+ ]
+ const actionsMenu = ( )
+
+ const transformDataTable = (d: SetupKey[]): SetupKeyDataTable[] => {
+ return d.map(p => ({...p, groupsCount: p.auto_groups ? p.auto_groups.length : 0} as SetupKeyDataTable))
}
useEffect(() => {
- dispatch(setupKeyActions.getSetupKeys.request({getAccessTokenSilently:accessToken, payload: null}));
+ dispatch(setupKeyActions.getSetupKeys.request({getAccessTokenSilently: accessToken, payload: null}));
+ dispatch(groupActions.getGroups.request({getAccessTokenSilently: accessToken, payload: null}));
}, [])
useEffect(() => {
@@ -93,52 +103,61 @@ export const SetupKeys = () => {
const deleteKey = 'deleting';
useEffect(() => {
if (deletedSetupKey.loading) {
- message.loading({ content: 'Deleting...', key: deleteKey, style: styleNotification });
+ message.loading({content: 'Deleting...', key: deleteKey, style: styleNotification});
} else if (deletedSetupKey.success) {
- message.success({ content: 'Setup key has been successfully removed.', key: deleteKey, duration: 2, style: styleNotification });
- dispatch(setupKeyActions.setDeleteSetupKey({ ...deletedSetupKey, success: false }))
+ message.success({
+ content: 'Setup key has been successfully removed.',
+ key: deleteKey,
+ duration: 2,
+ style: styleNotification
+ });
+ dispatch(setupKeyActions.setDeleteSetupKey({...deletedSetupKey, success: false}))
dispatch(setupKeyActions.resetDeletedSetupKey(null))
} else if (deletedSetupKey.error) {
- message.error({ content: 'Failed to delete setup key. You might not have enough permissions.', key: deleteKey, duration: 2, style: styleNotification });
- dispatch(setupKeyActions.setDeleteSetupKey({ ...deletedSetupKey, error: null }))
+ message.error({
+ content: 'Failed to delete setup key. You might not have enough permissions.',
+ key: deleteKey,
+ duration: 2,
+ style: styleNotification
+ });
+ dispatch(setupKeyActions.setDeleteSetupKey({...deletedSetupKey, error: null}))
dispatch(setupKeyActions.resetDeletedSetupKey(null))
}
}, [deletedSetupKey])
- const revokeKey = 'revoking';
+ const createKey = 'saving';
useEffect(() => {
- if (revokedSetupKey.loading) {
- message.loading({ content: 'Revoking...', key: revokeKey, duration: 0, style: styleNotification })
- } else if (revokedSetupKey.success) {
- message.success({ content: 'Setup key has been successfully revoked.', key: revokeKey, duration: 2, style: styleNotification });
- dispatch(setupKeyActions.resetRevokedSetupKey(null))
- } else if (revokedSetupKey.error) {
- message.error({ content: 'Failed to revoke setup key. You might not have enough permissions.', key: revokeKey, duration: 2, style: styleNotification });
- dispatch(setupKeyActions.resetRevokedSetupKey(null))
- }
- }, [revokedSetupKey])
-
- const createKey = 'creating';
- useEffect(() => {
- if (createdSetupKey.loading) {
- message.loading({ content: 'Creating...', key: createKey, duration: 0, style: styleNotification });
- } else if (createdSetupKey.success) {
- message.success({ content: 'Setup key has been successfully created.', key: createKey, duration: 2, style: styleNotification });
+ if (savedSetupKey.loading) {
+ message.loading({content: 'Saving...', key: createKey, duration: 0, style: styleNotification});
+ } else if (savedSetupKey.success) {
+ message.success({
+ content: 'Setup key has been successfully saved.',
+ key: createKey,
+ duration: 2,
+ style: styleNotification
+ });
dispatch(setupKeyActions.setSetupNewKeyVisible(false));
- dispatch(setupKeyActions.setCreateSetupKey({ ...createdSetupKey, success: false }));
- } else if (createdSetupKey.error) {
- message.error({ content: 'Failed to create setup key. You might not have enough permissions.', key: createKey, duration: 2, style: styleNotification });
- dispatch(setupKeyActions.setCreateSetupKey({ ...createdSetupKey, error: null }));
+ dispatch(setupKeyActions.setSavedSetupKey({...savedSetupKey, success: false}));
+ dispatch(setupKeyActions.resetSavedSetupKey(null))
+ } else if (savedSetupKey.error) {
+ message.error({
+ content: 'Failed to update setup key. You might not have enough permissions.',
+ key: createKey,
+ duration: 2,
+ style: styleNotification
+ });
+ dispatch(setupKeyActions.setSavedSetupKey({...savedSetupKey, error: null}));
+ dispatch(setupKeyActions.resetSavedSetupKey(null))
}
- }, [createdSetupKey])
+ }, [savedSetupKey])
- const filterDataTable = ():SetupKey[] => {
+ const filterDataTable = (): SetupKey[] => {
const t = textToSearch.toLowerCase().trim()
- let f:SetupKey[] = [...setupKeys]
+ let f: SetupKey[] = [...setupKeys]
if (optionValidAll === "valid") {
- f = filter(setupKeys, (_f:SetupKey) => _f.valid && !_f.revoked)
+ f = filter(setupKeys, (_f: SetupKey) => _f.valid && !_f.revoked)
}
- f = filter(f, (_f:SetupKey) =>
+ f = filter(f, (_f: SetupKey) =>
(_f.name.toLowerCase().includes(t) || _f.state.includes(t) || _f.type.toLowerCase().includes(t) || _f.key.toLowerCase().includes(t) || t === "")
) as SetupKey[]
return f
@@ -153,7 +172,7 @@ export const SetupKeys = () => {
setDataTable(transformDataTable(data))
}
- const onChangeValidAll = ({ target: { value } }: RadioChangeEvent) => {
+ const onChangeValidAll = ({target: {value}}: RadioChangeEvent) => {
setOptionValidAll(value)
}
@@ -163,7 +182,7 @@ export const SetupKeys = () => {
const showConfirmDelete = () => {
confirm({
- icon: ,
+ icon: ,
width: 600,
content:
{setupKeyToAction &&
@@ -175,7 +194,10 @@ export const SetupKeys = () => {
,
okType: 'danger',
onOk() {
- dispatch(setupKeyActions.deleteSetupKey.request({getAccessTokenSilently:accessToken, payload: setupKeyToAction ? setupKeyToAction.id : ''}));
+ dispatch(setupKeyActions.deleteSetupKey.request({
+ getAccessTokenSilently: accessToken,
+ payload: setupKeyToAction ? setupKeyToAction.id : ''
+ }));
},
onCancel() {
setSetupKeyToAction(null);
@@ -185,7 +207,7 @@ export const SetupKeys = () => {
const showConfirmRevoke = () => {
confirm({
- icon: ,
+ icon: ,
width: 600,
content:
{setupKeyToAction &&
@@ -197,7 +219,15 @@ export const SetupKeys = () => {
,
okType: 'danger',
onOk() {
- dispatch(setupKeyActions.revokeSetupKey.request({getAccessTokenSilently:accessToken, payload: { id: setupKeyToAction ? setupKeyToAction.id : null,revoked: true } as SetupKeyRevoke}));
+ dispatch(setupKeyActions.saveSetupKey.request({
+ getAccessTokenSilently: accessToken,
+ payload: {
+ id: setupKeyToAction ? setupKeyToAction.id : null,
+ revoked: true,
+ name: setupKeyToAction ? setupKeyToAction.name : null,
+ auto_groups: setupKeyToAction && setupKeyToAction.auto_groups ? setupKeyToAction.auto_groups : [],
+ } as SetupKeyToSave
+ }));
},
onCancel() {
setSetupKeyToAction(null);
@@ -206,25 +236,124 @@ export const SetupKeys = () => {
}
const onClickAddNewSetupKey = () => {
+ const autoGroups : string[] = []
dispatch(setupKeyActions.setSetupNewKeyVisible(true));
dispatch(setupKeyActions.setSetupKey({
- name: '',
- type: 'reusable'
+ name: "",
+ type: "reusable",
+ auto_groups: autoGroups
} as SetupKey))
}
+ const setKeyAndView = (key: SetupKeyDataTable) => {
+ dispatch(setupKeyActions.setSetupNewKeyVisible(true));
+ dispatch(setupKeyActions.setSetupKey({
+ id: key?.id || null,
+ key: key?.key,
+ name: key?.name,
+ revoked: key?.revoked,
+ expires: key?.expires,
+ state: key?.state,
+ type: key?.type,
+ used_times: key?.used_times,
+ valid: key?.valid,
+ auto_groups: key?.auto_groups,
+ last_used: key?.last_used,
+ } as SetupKey))
+ }
+
+ const onClickEditSetupKey = () => {
+ dispatch(setupKeyActions.setSetupNewKeyVisible(true));
+ dispatch(setupKeyActions.setSetupKey({
+ id: setupKeyToAction?.id || null,
+ key: setupKeyToAction?.key,
+ name: setupKeyToAction?.name,
+ revoked: setupKeyToAction?.revoked,
+ expires: setupKeyToAction?.expires,
+ state: setupKeyToAction?.state,
+ type: setupKeyToAction?.type,
+ used_times: setupKeyToAction?.used_times,
+ valid: setupKeyToAction?.valid,
+ auto_groups: setupKeyToAction?.auto_groups,
+ last_used: setupKeyToAction?.last_used,
+ } as SetupKey))
+ }
+
+ const renderPopoverGroups = (label: string, rowGroups: string[] | string[] | null, setupKeyToAction: SetupKeyDataTable) => {
+
+ let groupsMap = new Map();
+ 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)!)
+ }
+
+ let btn = setUpdateGroupsVisible(setupKeyToAction, true)}>{displayGroups.length}
+ if (!displayGroups || displayGroups!.length < 1) {
+ return btn
+ }
+
+ const content = displayGroups?.map((g, i) => {
+ const _g = g as Group
+ const peersCount = ` - ${_g.peers_count || 0} ${(!_g.peers_count || parseInt(_g.peers_count) !== 1) ? 'peers' : 'peer'} `
+ return (
+
+
+ {_g.name}
+
+ {peersCount}
+
+ )
+ })
+ const mainContent = ({content} )
+ let popoverPlacement = "top"
+ if (content && content.length > 5) {
+ popoverPlacement = "rightTop"
+ }
+
+ return (
+
+ {btn}
+
+ )
+ }
+
+ const setUpdateGroupsVisible = (setupKeyToAction: SetupKey, status: boolean) => {
+ if (status) {
+ dispatch(setupKeyActions.setSetupKey({...setupKeyToAction}))
+ dispatch(setupKeyActions.setSetupNewKeyVisible(true))
+ return
+ }
+ const autoGroups : string[] = []
+ dispatch(setupKeyActions.setSetupKey({
+ name: "",
+ type: "reusable",
+ auto_groups: autoGroups
+ } as SetupKey))
+ dispatch(setupKeyActions.setSetupNewKeyVisible(false))
+ }
+
return (
<>
Setup Keys
- A list of all the setup keys in your account including their name, state, type and expiration.
-
+ A list of all the setup keys in your account including their name, state, type and
+ expiration.
+
{/* */}
-
+
@@ -235,7 +364,8 @@ export const SetupKeys = () => {
optionType="button"
buttonStyle="solid"
/>
-
+
{
{failed &&
-
+
}
`Showing ${range[0]} to ${range[1]} of ${total} setup keys`)}}
+ pagination={{
+ pageSize,
+ showSizeChanger: false,
+ showTotal: ((total, range) => `Showing ${range[0]} to ${range[1]} of ${total} setup keys`)
+ }}
className="card-table"
showSorterTooltip={false}
scroll={{x: true}}
@@ -265,12 +400,18 @@ export const SetupKeys = () => {
(record as any).name.includes(value)}
sorter={(a, b) => ((a as any).name.localeCompare((b as any).name))}
+ render={(text, record, index) => {
+ return setKeyAndView(record as SetupKeyDataTable)}
+ className="tooltip-label">{text}
+ }}
defaultSortOrder='ascend'
/>
{
- return (text === 'valid') ? {text} : {text}
+ return (text === 'valid') ? {text} :
+ {text}
}}
sorter={(a, b) => ((a as any).state.localeCompare((b as any).state))}
/>
@@ -279,19 +420,25 @@ export const SetupKeys = () => {
onFilter={(value: string | number | boolean, record) => (record as any).type.includes(value)}
sorter={(a, b) => ((a as any).type.localeCompare((b as any).type))}
/>
-
+ {
+ return renderPopoverGroups(text, record.auto_groups, record)
+ }}
+ />
(record as any).key.includes(value)}
sorter={(a, b) => ((a as any).key.localeCompare((b as any).key))}
render={(text, record, index) => {
- return
+ return
}}
/>
((a as any).last_used.localeCompare((b as any).last_used))}
render={(text, record, index) => {
- return !(record as SetupKey).used_times ? 'unused' : timeAgo(text)
+ return !(record as SetupKey).used_times ? 'never' : timeAgo(text)
}}
/>
{
{
return !(record as SetupKeyDataTable).revoked ? (
- {
- if (visible) setSetupKeyToAction(record as SetupKeyDataTable)
- }}> ) : <>>
+ {
+ if (visible) setSetupKeyToAction(record as SetupKeyDataTable)
+ }}> ) : <>>
}}
/>