From 86bb3de7e2c3160cd01a866e2674cdf4695c369b Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 15 Jun 2022 23:03:27 +0200 Subject: [PATCH 01/10] Add SSH server switch --- src/store/peer/types.ts | 1 + src/views/Peers.tsx | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/store/peer/types.ts b/src/store/peer/types.ts index b6fcf8c0..3317d5ad 100644 --- a/src/store/peer/types.ts +++ b/src/store/peer/types.ts @@ -9,6 +9,7 @@ export interface Peer { os: string, version: string, groups?: Group[] + ssh_enabled: boolean, } export interface PeerToSave extends Peer { diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index 2e35e3fc..ee37390e 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -20,7 +20,7 @@ import { RadioChangeEvent, Dropdown, Menu, - Alert, Select, Modal, Button, message, Popover, SpinProps, Spin + Alert, Select, Modal, Button, message, Popover, SpinProps, Spin, Switch } from "antd"; import {Peer} from "../store/peer/types"; import {filter} from "lodash" @@ -216,6 +216,10 @@ export const Peers = () => { ) } + function handleSwitchSSH(record :PeerDataTable) { + console.log(record.ssh_enabled); + } + return ( <> @@ -282,16 +286,21 @@ export const Peers = () => { return }} /> - { return text ? online : offline }} /> - { return renderPopoverGroups(text, record.groups, record) }} /> + ( + handleSwitchSSH(record)} defaultChecked={text} />) + } + /> { return (record as PeerDataTable).connected ? 'just now' : timeAgo(text) From a3c060bf34b8bbb75b34d5e09eceb4fff73a61ba Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 16 Jun 2022 22:44:59 +0200 Subject: [PATCH 02/10] Enable/Disable SSH server on peer --- src/store/group/actions.ts | 8 ++++++- src/store/peer/actions.ts | 17 +++++++++++++-- src/store/peer/sagas.ts | 44 +++++++++++++++++++++++++++++++++++++- src/store/peer/service.ts | 9 ++++++++ src/views/Peers.tsx | 17 ++++++++++----- 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/store/group/actions.ts b/src/store/group/actions.ts index 0c4b1a45..8ff14934 100644 --- a/src/store/group/actions.ts +++ b/src/store/group/actions.ts @@ -1,6 +1,12 @@ import { ActionType, createAction, createAsyncAction } from 'typesafe-actions'; import { Group } from './types'; -import {ApiError, CreateResponse, DeleteResponse, RequestPayload} from '../../services/api-client/types'; +import { + ApiError, + ChangeResponse, + CreateResponse, + DeleteResponse, + RequestPayload +} from '../../services/api-client/types'; const actions = { getGroups: createAsyncAction( diff --git a/src/store/peer/actions.ts b/src/store/peer/actions.ts index 687cbc4d..00452855 100644 --- a/src/store/peer/actions.ts +++ b/src/store/peer/actions.ts @@ -1,7 +1,14 @@ import { ActionType, createAction, createAsyncAction } from 'typesafe-actions'; import {Peer, PeerGroupsToSave} from './types'; -import {ApiError, ChangeResponse, DeleteResponse, RequestPayload} from '../../services/api-client/types'; +import { + ApiError, + ChangeResponse, + CreateResponse, + DeleteResponse, + RequestPayload +} from '../../services/api-client/types'; import {Group} from "../group/types"; +import {SetupKey} from "../setup-key/types"; const actions = { getPeers: createAsyncAction( @@ -28,7 +35,13 @@ const actions = { removePeer: createAction('REMOVE_PEER')(), setPeer: createAction('SET_PEER')(), - setUpdateGroupsVisible: createAction('SET_UPDATE_GROUPS_VISIBLE')() + setUpdateGroupsVisible: createAction('SET_UPDATE_GROUPS_VISIBLE')(), + updatePeer: createAsyncAction( + 'UPDATE_PEER', + 'UPDATE_PEER_SUCCESS', + 'UPDATE_PEER_FAILURE', + ), ChangeResponse, ChangeResponse>(), + setUpdatedPeer: createAction('SET_UPDATED_PEER')>(), }; export type ActionTypes = ActionType; diff --git a/src/store/peer/sagas.ts b/src/store/peer/sagas.ts index 9c87d079..8a593b81 100644 --- a/src/store/peer/sagas.ts +++ b/src/store/peer/sagas.ts @@ -13,6 +13,7 @@ import actions from './actions'; import {Group, GroupPeer} from "../group/types"; import serviceGroup from "../group/service"; import {actions as groupActions} from "../group"; +import {Rule} from "../rule/types"; export function* getPeers(action: ReturnType): Generator { @@ -153,11 +154,52 @@ export function* saveGroups(action: ReturnType): Generator { + try { + yield put(actions.setUpdatedPeer({ + loading: true, + success: false, + failure: false, + error: null, + data: null + })) + + const peer = action.payload.payload + + const payloadToSave = { + getAccessTokenSilently: action.payload.getAccessTokenSilently, + payload: peer + } + + const effect = yield call(service.updatePeer, payloadToSave) + const response = effect as ApiResponse; + + yield put(actions.updatePeer.success({ + loading: false, + success: true, + failure: false, + error: null, + data: response.body + } as ChangeResponse)); + + } catch (err) { + console.log(err) + yield put(actions.updatePeer.failure({ + loading: false, + success: false, + failure: true, + error: err as ApiError, + data: null + } as ChangeResponse)); + } +} + export default function* sagas(): Generator { yield all([ takeLatest(actions.getPeers.request, getPeers), takeLatest(actions.deletedPeer.request, deletePeer), - takeLatest(actions.saveGroups.request, saveGroups) + takeLatest(actions.saveGroups.request, saveGroups), + takeLatest(actions.updatePeer.request, updatePeer) ]); } diff --git a/src/store/peer/service.ts b/src/store/peer/service.ts index 8aab91be..d641877c 100644 --- a/src/store/peer/service.ts +++ b/src/store/peer/service.ts @@ -1,6 +1,7 @@ import {ApiResponse, RequestPayload} from '../../services/api-client/types'; import { apiClient } from '../../services/api-client'; import { Peer } from './types'; +import {Rule} from "../rule/types"; export default { async getPeers(payload:RequestPayload): Promise> { @@ -14,5 +15,13 @@ export default { `/api/peers/` + payload.payload, payload ); + }, + async updatePeer(payload:RequestPayload): Promise> { + const id = payload.payload.id + delete payload.payload.id + return apiClient.put( + `/api/peers/${id}`, + payload + ); } }; diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index ee37390e..94c7cd8e 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -22,7 +22,7 @@ import { Menu, Alert, Select, Modal, Button, message, Popover, SpinProps, Spin, Switch } from "antd"; -import {Peer} from "../store/peer/types"; +import {Peer, PeerToSave} from "../store/peer/types"; import {filter} from "lodash" import {formatOS, timeAgo} from "../utils/common"; import {ExclamationCircleOutlined} from "@ant-design/icons"; @@ -30,6 +30,7 @@ import ButtonCopyMessage from "../components/ButtonCopyMessage"; import {Group, GroupPeer} from "../store/group/types"; import PeerGroupsUpdate from "../components/PeerGroupsUpdate"; import tableSpin from "../components/Spin"; +import {RuleToSave} from "../store/rule/types"; const { Title, Paragraph } = Typography; const { Column } = Table; @@ -216,8 +217,14 @@ export const Peers = () => { ) } - function handleSwitchSSH(record :PeerDataTable) { - console.log(record.ssh_enabled); + + function handleSwitchSSH(record: PeerDataTable, checked: boolean) { + const peer = { + id: record.id, + ssh_enabled: checked, + name: record.name + } as Peer + dispatch(peerActions.updatePeer.request({getAccessTokenSilently, payload: peer})); } return ( @@ -297,8 +304,8 @@ export const Peers = () => { }} /> ( - handleSwitchSSH(record)} defaultChecked={text} />) + render={(e, record:PeerDataTable, index) => ( + handleSwitchSSH(record, checked)} defaultChecked={e} />) } /> Date: Thu, 16 Jun 2022 23:30:10 +0200 Subject: [PATCH 03/10] Enable/Disable SSH server on peer --- src/store/peer/actions.ts | 3 ++- src/store/peer/reducer.ts | 23 ++++++++++++++++++++--- src/store/peer/sagas.ts | 3 +++ src/views/AccessControl.tsx | 3 --- src/views/Peers.tsx | 22 +++++++++++++++++++--- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/store/peer/actions.ts b/src/store/peer/actions.ts index 00452855..e26c3f62 100644 --- a/src/store/peer/actions.ts +++ b/src/store/peer/actions.ts @@ -41,7 +41,8 @@ const actions = { 'UPDATE_PEER_SUCCESS', 'UPDATE_PEER_FAILURE', ), ChangeResponse, ChangeResponse>(), - setUpdatedPeer: createAction('SET_UPDATED_PEER')>(), + setUpdatedPeer: createAction('SET_UPDATED_PEER')>(), + resetUpdatedPeer: createAction('RESET_UPDATED_PEER')(), }; export type ActionTypes = ActionType; diff --git a/src/store/peer/reducer.ts b/src/store/peer/reducer.ts index 7bbaa5d7..b1e997e0 100644 --- a/src/store/peer/reducer.ts +++ b/src/store/peer/reducer.ts @@ -2,8 +2,9 @@ import { createReducer } from 'typesafe-actions'; import { combineReducers } from 'redux'; import { Peer } from './types'; import actions, { ActionTypes } from './actions'; -import {ApiError, ChangeResponse, DeleteResponse} from "../../services/api-client/types"; +import {ApiError, ChangeResponse, CreateResponse, DeleteResponse} from "../../services/api-client/types"; import {Group} from "../group/types"; +import {Rule} from "../rule/types"; type StateType = Readonly<{ data: Peer[] | null; @@ -14,6 +15,7 @@ type StateType = Readonly<{ deletedPeer: DeleteResponse; setUpdateGroupsVisible: boolean; savedGroups: ChangeResponse; + updatedPeer: CreateResponse; }>; const initialState: StateType = { @@ -36,7 +38,14 @@ const initialState: StateType = { failure: false, error: null, data: null - } + }, + updatedPeer: >{ + loading: false, + success: false, + failure: false, + error: null, + data : null + }, }; const data = createReducer(initialState.data as Peer[]) @@ -77,6 +86,13 @@ const savedGroups = createReducer, ActionTypes>(i .handleAction(actions.saveGroups.failure, (store, action) => action.payload) .handleAction(actions.resetSavedGroups, () => initialState.savedGroups) +const updatedPeer = createReducer, ActionTypes>(initialState.updatedPeer) + .handleAction(actions.updatePeer.request, () => initialState.updatedPeer) + .handleAction(actions.updatePeer.success, (store, action) => action.payload) + .handleAction(actions.updatePeer.failure, (store, action) => action.payload) + .handleAction(actions.setUpdatedPeer, (store, action) => action.payload) + .handleAction(actions.resetUpdatedPeer, () => initialState.updatedPeer) + export default combineReducers({ data, peer, @@ -85,5 +101,6 @@ export default combineReducers({ saving, deletedPeer, updateGroupsVisible, - savedGroups + savedGroups, + updatedPeer }); diff --git a/src/store/peer/sagas.ts b/src/store/peer/sagas.ts index 8a593b81..6bdb5293 100644 --- a/src/store/peer/sagas.ts +++ b/src/store/peer/sagas.ts @@ -165,6 +165,7 @@ export function* updatePeer(action: ReturnType)); + yield put(actions.getPeers.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null })); + } catch (err) { console.log(err) yield put(actions.updatePeer.failure({ diff --git a/src/views/AccessControl.tsx b/src/views/AccessControl.tsx index 84d2ed42..6b318d33 100644 --- a/src/views/AccessControl.tsx +++ b/src/views/AccessControl.tsx @@ -19,12 +19,9 @@ import {CloseOutlined, ExclamationCircleOutlined} from "@ant-design/icons"; import bidirect from '../assets/direct_bi.svg'; import inbound from '../assets/direct_in.svg'; import outbound from '../assets/direct_out.svg'; -import tutorial from "../assets/access_control_tutorial.svg"; import AccessControlNew from "../components/AccessControlNew"; import {Group} from "../store/group/types"; -import {actions as setupKeyActions} from "../store/setup-key"; import AccessControlModalGroups from "../components/AccessControlModalGroups"; -import TableSpin from "../components/Spin"; import tableSpin from "../components/Spin"; const { Title, Paragraph } = Typography; diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index 94c7cd8e..5fb5d96a 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -22,7 +22,7 @@ import { Menu, Alert, Select, Modal, Button, message, Popover, SpinProps, Spin, Switch } from "antd"; -import {Peer, PeerToSave} from "../store/peer/types"; +import {Peer} from "../store/peer/types"; import {filter} from "lodash" import {formatOS, timeAgo} from "../utils/common"; import {ExclamationCircleOutlined} from "@ant-design/icons"; @@ -30,7 +30,6 @@ import ButtonCopyMessage from "../components/ButtonCopyMessage"; import {Group, GroupPeer} from "../store/group/types"; import PeerGroupsUpdate from "../components/PeerGroupsUpdate"; import tableSpin from "../components/Spin"; -import {RuleToSave} from "../store/rule/types"; const { Title, Paragraph } = Typography; const { Column } = Table; @@ -53,6 +52,7 @@ export const Peers = () => { const groups = useSelector((state: RootState) => state.group.data); const loadingGroups = useSelector((state: RootState) => state.group.loading); const savedGroups = useSelector((state: RootState) => state.peer.savedGroups); + const updatedPeer = useSelector((state: RootState) => state.peer.updatedPeer); const [textToSearch, setTextToSearch] = useState(''); const [optionOnOff, setOptionOnOff] = useState('all'); @@ -133,6 +133,22 @@ export const Peers = () => { } }, [savedGroups]) + const updatePeerKey = 'updating_peer'; + useEffect(() => { + const style = { marginTop: 85 } + if (updatedPeer.loading) { + message.loading({ content: 'Updating peer...', key: updatePeerKey, duration: 0, style }) + } else if (updatedPeer.success) { + message.success({ content: 'Peer has been successfully updated.', key: updatePeerKey, duration: 2, style }); + dispatch(peerActions.setUpdatedPeer({ ...updatedPeer, success: false })) + dispatch(peerActions.resetUpdatedPeer(null)) + } else if (updatedPeer.error) { + message.error({ content: 'Failed to update peer. You might not have enough permissions.', key: updatePeerKey, duration: 2, style }); + dispatch(peerActions.setUpdatedPeer({ ...updatedPeer, error: null })) + dispatch(peerActions.resetUpdatedPeer(null)) + } + }, [updatedPeer]) + const filterDataTable = ():Peer[] => { const t = textToSearch.toLowerCase().trim() let f:Peer[] = filter(peers, (f:Peer) => @@ -305,7 +321,7 @@ export const Peers = () => { /> ( - handleSwitchSSH(record, checked)} defaultChecked={e} />) + handleSwitchSSH(record, checked)} defaultChecked={e}/>) } /> Date: Fri, 17 Jun 2022 01:22:48 +0200 Subject: [PATCH 04/10] Avoid double API call when updating peers --- src/store/peer/sagas.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/store/peer/sagas.ts b/src/store/peer/sagas.ts index 6bdb5293..653948f7 100644 --- a/src/store/peer/sagas.ts +++ b/src/store/peer/sagas.ts @@ -14,6 +14,7 @@ import {Group, GroupPeer} from "../group/types"; import serviceGroup from "../group/service"; import {actions as groupActions} from "../group"; import {Rule} from "../rule/types"; +import peers from "../../views/Peers"; export function* getPeers(action: ReturnType): Generator { @@ -183,7 +184,8 @@ export function* updatePeer(action: ReturnType)); - yield put(actions.getPeers.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null })); + const peers = (yield select(state => state.peer.data)) as Peer[] + yield put(actions.getPeers.success(peers.filter((p:Peer) => p.id !== peerId).concat(response.body))) } catch (err) { console.log(err) From cbb6fc12693839cf3815dd84cdca23288a769ec4 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 22:29:33 +0200 Subject: [PATCH 05/10] Add experimental feature tooltip to SSH --- src/views/Peers.tsx | 51 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index eca438ab..2f226931 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -2,30 +2,37 @@ import React, {useEffect, useState} from 'react'; import {Link} from 'react-router-dom'; import {useDispatch, useSelector} from "react-redux"; import {useAuth0, withAuthenticationRequired} from "@auth0/auth0-react"; -import { RootState } from "typesafe-actions"; -import { actions as peerActions } from '../store/peer'; -import { actions as groupActions } from '../store/group'; +import {RootState} from "typesafe-actions"; +import {actions as peerActions} from '../store/peer'; +import {actions as groupActions} from '../store/group'; import Loading from "../components/Loading"; import {Container} from "../components/Container"; 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, Popover, SpinProps, Spin, Switch + Row, + Select, + Space, + Switch, + Table, + Tag, + Typography, + Tooltip } from "antd"; import {Peer} from "../store/peer/types"; import {filter} from "lodash" import {formatOS, timeAgo} from "../utils/common"; -import {ExclamationCircleOutlined} from "@ant-design/icons"; +import Icon, {ExclamationCircleOutlined, QuestionCircleOutlined, WarningOutlined} from "@ant-design/icons"; import ButtonCopyMessage from "../components/ButtonCopyMessage"; import {Group, GroupPeer} from "../store/group/types"; import PeerGroupsUpdate from "../components/PeerGroupsUpdate"; @@ -69,6 +76,15 @@ export const Peers = () => { const optionsOnOff = [{label: 'Online', value: 'on'},{label: 'All', value: 'all'}] + const sshColumnTitle = ( + + SSH Server + + + + + ) + const itemsMenuAction = [ { key: "delete", @@ -325,7 +341,14 @@ export const Peers = () => { return renderPopoverGroups(text, record.groups, record) }} /> - ( + handleSwitchSSH(record, checked)} defaultChecked={e}/>) + } + />*/} + ( handleSwitchSSH(record, checked)} defaultChecked={e}/>) } From 545db43ac491e35842686043420e5b8f00abed2f Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Thu, 23 Jun 2022 13:20:09 +0200 Subject: [PATCH 06/10] Added confirmation box for SSH and updated Delete msg box --- src/views/Peers.tsx | 74 ++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index 2f226931..9bcaab6d 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -76,15 +76,6 @@ export const Peers = () => { const optionsOnOff = [{label: 'Online', value: 'on'},{label: 'All', value: 'all'}] - const sshColumnTitle = ( - - SSH Server - - - - - ) - const itemsMenuAction = [ { key: "delete", @@ -195,17 +186,12 @@ export const Peers = () => { } const showConfirmDelete = () => { + let name = peerToAction ? peerToAction.name : '' confirm({ icon: , + title: "Delete peer \"" + name + "\"", width: 600, - content: - {peerToAction && - <> - Delete peer "{peerToAction ? peerToAction.name : ''}" - Are you sure you want to delete peer from your account? - - } - , + content: "Are you sure you want to delete peer from your account?", okType: 'danger', onOk() { dispatch(peerActions.deletedPeer.request({getAccessTokenSilently, payload: peerToAction ? peerToAction.ip : ''})); @@ -216,6 +202,30 @@ export const Peers = () => { }); } + const showConfirmEnableSSH = (record: PeerDataTable) => { + confirm({ + icon: , + title: "Enable SSH Service for \"" + record.name + "\"?", + width: 600, + content: "Experimental feature. Enabling this option allows remote SSH access to this machine from other connected network participants.", + okType: 'danger', + onOk() { + + handleSwitchSSH(record, true) + }, + onCancel() { + }, + }); + } + function handleSwitchSSH(record: PeerDataTable, checked: boolean) { + const peer = { + id: record.id, + ssh_enabled: checked, + name: record.name + } as Peer + dispatch(peerActions.updatePeer.request({getAccessTokenSilently, payload: peer})); + + } const setUpdateGroupsVisible = (peerToAction:Peer, status:boolean) => { if (status) { dispatch(peerActions.setPeer({...peerToAction})) @@ -255,16 +265,6 @@ export const Peers = () => { ) } - - function handleSwitchSSH(record: PeerDataTable, checked: boolean) { - const peer = { - id: record.id, - ssh_enabled: checked, - name: record.name - } as Peer - dispatch(peerActions.updatePeer.request({getAccessTokenSilently, payload: peer})); - } - return ( <> @@ -341,18 +341,22 @@ export const Peers = () => { return renderPopoverGroups(text, record.groups, record) }} /> - {/* ( - handleSwitchSSH(record, checked)} defaultChecked={e}/>) - } - />*/} ( - handleSwitchSSH(record, checked)} defaultChecked={e}/>) + + { + if (checked) { + showConfirmEnableSSH(record) + } else { + handleSwitchSSH(record, checked) + } + }} + />) } /> + { return (record as PeerDataTable).connected ? 'just now' : timeAgo(text) From c9837f84a6af60ea6d4cce698d090f434ad6f0aa Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Thu, 23 Jun 2022 14:56:17 +0200 Subject: [PATCH 07/10] Use SSH Server in tittle --- src/views/Peers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Peers.tsx b/src/views/Peers.tsx index 9bcaab6d..8af0ffc8 100644 --- a/src/views/Peers.tsx +++ b/src/views/Peers.tsx @@ -205,7 +205,7 @@ export const Peers = () => { const showConfirmEnableSSH = (record: PeerDataTable) => { confirm({ icon: , - title: "Enable SSH Service for \"" + record.name + "\"?", + title: "Enable SSH Server for \"" + record.name + "\"?", width: 600, content: "Experimental feature. Enabling this option allows remote SSH access to this machine from other connected network participants.", okType: 'danger', From 39843df0816080df793f6656769d25e2a9999857 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 16:07:33 +0200 Subject: [PATCH 08/10] Disable SSH server support on Windows --- src/components/addpeer/LinuxTab.tsx | 2 +- src/views/Peers.tsx | 31 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/components/addpeer/LinuxTab.tsx b/src/components/addpeer/LinuxTab.tsx index 01cad652..6de57adf 100644 --- a/src/components/addpeer/LinuxTab.tsx +++ b/src/components/addpeer/LinuxTab.tsx @@ -10,7 +10,7 @@ export const OtherTab = () => { const [steps, _] = useState([ { key: 1, - title: 'For different installation options check out our documentation.', + title: 'For other installation options check our documentation.', commands: (