WIP network tunnel stuff
This commit is contained in:
parent
29bc4a4abf
commit
5dc06023be
|
@ -89,14 +89,9 @@ class Cards extends React.Component {
|
|||
fontSize: '0.75em',
|
||||
},
|
||||
}, [
|
||||
JSON.stringify(this.props, null, 2),
|
||||
JSON.stringify(this.props.cards, null, 2),
|
||||
]),
|
||||
] : [
|
||||
!this.props.preferences.hideOnScreenButtons && r(Button, {
|
||||
autoFocus: true,
|
||||
onClick: toggle,
|
||||
}, 'Cards'),
|
||||
]);
|
||||
] : []);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -452,16 +452,36 @@ const Icon = ({ state, name, ...props }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const DebugText = ({ dgo, pai, state }) => r.div({
|
||||
style: {
|
||||
fontSize: '50%',
|
||||
},
|
||||
}, state.preferences.showDebugInfo ? [
|
||||
JSON.stringify(dgo, null, 2),
|
||||
JSON.stringify(pai, null, 2),
|
||||
] : []);
|
||||
const RemoteTunnelInfo = ({ pai }) => {
|
||||
const fqdn = path([ 'properties', 'tunnel', 'remote', 'fqdn' ], pai);
|
||||
|
||||
const SinkText = ({ dgo, pai, state, selected }) => r.div([
|
||||
if (!fqdn) {
|
||||
return r(React.Fragment);
|
||||
}
|
||||
|
||||
return r.div({
|
||||
className: 'node-tunnel-info',
|
||||
}, [
|
||||
fqdn,
|
||||
]);
|
||||
};
|
||||
|
||||
const DebugText = ({ dgo, pai, state }) => {
|
||||
if (!state.preferences.showDebugInfo) {
|
||||
return r(React.Fragment);
|
||||
}
|
||||
|
||||
return r.div({
|
||||
style: {
|
||||
fontSize: '50%',
|
||||
},
|
||||
}, [
|
||||
JSON.stringify(dgo, null, 2),
|
||||
JSON.stringify(pai, null, 2),
|
||||
]);
|
||||
};
|
||||
|
||||
const SinkText = ({ dgo, pai, state, selected }) => r(React.Fragment, [
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
}, [
|
||||
|
@ -479,10 +499,11 @@ const SinkText = ({ dgo, pai, state, selected }) => r.div([
|
|||
]),
|
||||
!selected && r(VolumeThumbnail, { pai, state }),
|
||||
selected && r(VolumeControls, { pai, state }),
|
||||
r(RemoteTunnelInfo, { pai }),
|
||||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
const SourceText = ({ dgo, pai, state, selected }) => r.div([
|
||||
const SourceText = ({ dgo, pai, state, selected }) => r(React.Fragment, [
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
}, [
|
||||
|
@ -500,10 +521,11 @@ const SourceText = ({ dgo, pai, state, selected }) => r.div([
|
|||
]),
|
||||
!selected && r(VolumeThumbnail, { pai, state }),
|
||||
selected && r(VolumeControls, { pai, state }),
|
||||
r(RemoteTunnelInfo, { pai }),
|
||||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
const ClientText = ({ dgo, pai, state }) => r.div([
|
||||
const ClientText = ({ dgo, pai, state }) => r(React.Fragment, [
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
title: path('properties.application.process.binary'.split('.'), pai),
|
||||
|
@ -511,7 +533,7 @@ const ClientText = ({ dgo, pai, state }) => r.div([
|
|||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
const ModuleText = ({ dgo, pai, state }) => r.div([
|
||||
const ModuleText = ({ dgo, pai, state }) => r(React.Fragment, [
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
title: pai.properties.module.description,
|
||||
|
|
|
@ -16,6 +16,7 @@ const keyMap = {
|
|||
hotKeyEscape: 'escape',
|
||||
|
||||
hotKeyFocusCards: 'c',
|
||||
hotKeyFocusNetwork: 'n',
|
||||
hotKeyFocusGraph: 'g',
|
||||
hotKeyFocusPreferences: 'p',
|
||||
|
||||
|
@ -47,6 +48,7 @@ class MyHotKeys extends React.Component {
|
|||
|
||||
this.graphRef = React.createRef();
|
||||
this.cardsRef = React.createRef();
|
||||
this.networkRef = React.createRef();
|
||||
this.preferencesRef = React.createRef();
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,15 @@ class MyHotKeys extends React.Component {
|
|||
this.hotKeyFocusGraph();
|
||||
}
|
||||
|
||||
hotKeyFocusGraph() {
|
||||
this.cardsRef.current.getWrappedInstance().close();
|
||||
this.networkRef.current.getWrappedInstance().close();
|
||||
this.preferencesRef.current.getWrappedInstance().close();
|
||||
this.graphRef.current.getWrappedInstance().focus();
|
||||
}
|
||||
|
||||
hotKeyFocusCards() {
|
||||
this.networkRef.current.getWrappedInstance().close();
|
||||
this.preferencesRef.current.getWrappedInstance().close();
|
||||
|
||||
const cards = this.cardsRef.current.getWrappedInstance();
|
||||
|
@ -64,14 +74,20 @@ class MyHotKeys extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
hotKeyFocusGraph() {
|
||||
hotKeyFocusNetwork() {
|
||||
this.cardsRef.current.getWrappedInstance().close();
|
||||
this.preferencesRef.current.getWrappedInstance().close();
|
||||
this.graphRef.current.getWrappedInstance().focus();
|
||||
|
||||
const network = this.networkRef.current.getWrappedInstance();
|
||||
network.toggle();
|
||||
if (!network.isOpen()) {
|
||||
this.graphRef.current.getWrappedInstance().focus();
|
||||
}
|
||||
}
|
||||
|
||||
hotKeyFocusPreferences() {
|
||||
this.cardsRef.current.getWrappedInstance().close();
|
||||
this.networkRef.current.getWrappedInstance().close();
|
||||
|
||||
const preferences = this.preferencesRef.current.getWrappedInstance();
|
||||
preferences.toggle();
|
||||
|
@ -92,11 +108,13 @@ class MyHotKeys extends React.Component {
|
|||
}, this.props.children({
|
||||
graphRef: this.graphRef,
|
||||
cardsRef: this.cardsRef,
|
||||
networkRef: this.networkRef,
|
||||
preferencesRef: this.preferencesRef,
|
||||
|
||||
actions: {
|
||||
focusGraph: handlers.hotKeyFocusGraph,
|
||||
focusCards: handlers.hotKeyFocusCards,
|
||||
focusNetwork: handlers.hotKeyFocusNetwork,
|
||||
focusPreferences: handlers.hotKeyFocusPreferences,
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
|
||||
const r = require('r-dom');
|
||||
|
||||
module.exports = props => r.label({
|
||||
className: 'label',
|
||||
module.exports = ({ userSelect, passive, ...props }) => r.label({
|
||||
classSet: {
|
||||
label: true,
|
||||
'label-user-select': userSelect,
|
||||
'label-passive': passive,
|
||||
},
|
||||
...props,
|
||||
}, props.children);
|
||||
|
|
|
@ -41,6 +41,10 @@ const WindowMenu = props => r(WindowMenuBase, [
|
|||
label: 'Cards',
|
||||
onClick: props.focusCards,
|
||||
}),
|
||||
r(MenuItem, {
|
||||
label: 'Network',
|
||||
onClick: props.focusNetwork,
|
||||
}),
|
||||
r(MenuItem, {
|
||||
label: 'Preferences',
|
||||
onClick: props.focusPreferences,
|
||||
|
|
|
@ -51,6 +51,8 @@ class Modals extends React.PureComponent {
|
|||
newGraphObjectModalOpen: false,
|
||||
loadModuleModalOpen: false,
|
||||
|
||||
modalDefaults: undefined,
|
||||
|
||||
actions: {
|
||||
openConnectToServerModal: this.openConnectToServerModal.bind(this),
|
||||
|
||||
|
@ -108,8 +110,11 @@ class Modals extends React.PureComponent {
|
|||
this.setState({ newGraphObjectModalOpen: true });
|
||||
}
|
||||
|
||||
openLoadModuleModal() {
|
||||
this.setState({ loadModuleModalOpen: true });
|
||||
openLoadModuleModal(modalDefaults) {
|
||||
this.setState({
|
||||
loadModuleModalOpen: true,
|
||||
modalDefaults,
|
||||
});
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
|
@ -145,9 +150,11 @@ class Modals extends React.PureComponent {
|
|||
openLoadModuleModal: this.state.actions.openLoadModuleModal,
|
||||
}),
|
||||
|
||||
r(LoadModuleModal, {
|
||||
isOpen: this.state.loadModuleModalOpen,
|
||||
this.state.loadModuleModalOpen && r(LoadModuleModal, {
|
||||
isOpen: true,
|
||||
onRequestClose: this.handleCancel,
|
||||
|
||||
defaults: this.state.modalDefaults,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ class LoadModuleModal extends React.PureComponent {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
args: '',
|
||||
name: props.defaults.name,
|
||||
args: props.defaults.args,
|
||||
};
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
|
@ -87,6 +87,13 @@ class LoadModuleModal extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
LoadModuleModal.defaultProps = {
|
||||
defaults: {
|
||||
name: '',
|
||||
args: '',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = connect(
|
||||
null,
|
||||
dispatch => bindActionCreators(pulseActions, dispatch),
|
||||
|
|
147
components/network/index.js
Normal file
147
components/network/index.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
const {
|
||||
values,
|
||||
map,
|
||||
path,
|
||||
filter,
|
||||
propEq,
|
||||
sortBy,
|
||||
prop,
|
||||
} = require('ramda');
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const r = require('r-dom');
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { bindActionCreators } = require('redux');
|
||||
|
||||
const { pulse: pulseActions } = require('../../actions');
|
||||
const { formatModuleArgs } = require('../../utils/module-args');
|
||||
|
||||
const Button = require('../button');
|
||||
const Label = require('../label');
|
||||
|
||||
class Cards extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
open: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setState({ open: !this.state.open });
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState({ open: false });
|
||||
}
|
||||
|
||||
isOpen() {
|
||||
return this.state.open;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { open } = this.state;
|
||||
const toggle = this.toggle.bind(this);
|
||||
|
||||
const nativeProtocolTcpModules = sortBy(prop('index'), filter(
|
||||
propEq('name', 'module-native-protocol-tcp'),
|
||||
values(this.props.modules),
|
||||
));
|
||||
|
||||
return r.div({
|
||||
classSet: {
|
||||
panel: true,
|
||||
cards: true,
|
||||
open,
|
||||
},
|
||||
}, open ? [
|
||||
!this.props.preferences.hideOnScreenButtons && r(React.Fragment, [
|
||||
r(Button, {
|
||||
style: { width: '100%' },
|
||||
autoFocus: true,
|
||||
onClick: toggle,
|
||||
}, 'Close'),
|
||||
|
||||
r.hr(),
|
||||
]),
|
||||
|
||||
nativeProtocolTcpModules.length > 0 ? r(React.Fragment, [
|
||||
r(Label, [
|
||||
'This server:',
|
||||
]),
|
||||
|
||||
...map(module => r.div([
|
||||
r.div({
|
||||
style: { display: 'flex', justifyContent: 'space-between' },
|
||||
}, [
|
||||
r(Label, {
|
||||
passive: true,
|
||||
}, [
|
||||
path([ 'properties', 'module', 'description' ], module),
|
||||
]),
|
||||
|
||||
r(Button, {
|
||||
onClick: () => {
|
||||
this.props.actions.unloadModuleByIndex(module.index);
|
||||
},
|
||||
}, 'Unload'),
|
||||
]),
|
||||
|
||||
r(Label, {
|
||||
userSelect: true,
|
||||
}, [
|
||||
r.code([
|
||||
module.name,
|
||||
' ',
|
||||
module.args,
|
||||
]),
|
||||
]),
|
||||
]), nativeProtocolTcpModules),
|
||||
]) : r(Label, {
|
||||
title: 'No loaded `module-native-protocol-tcp` found',
|
||||
}, [
|
||||
'This server does not currently accept tcp connections.',
|
||||
]),
|
||||
|
||||
r(Button, {
|
||||
onClick: () => {
|
||||
this.props.openLoadModuleModal({
|
||||
name: 'module-native-protocol-tcp',
|
||||
args: formatModuleArgs({
|
||||
'auth-ip-acl': [
|
||||
'127.0.0.0/8',
|
||||
'10.0.0.0/8',
|
||||
'172.16.0.0/12',
|
||||
'192.168.0.0/16',
|
||||
],
|
||||
}),
|
||||
});
|
||||
},
|
||||
}, 'Allow incoming connections...'),
|
||||
|
||||
this.props.preferences.showDebugInfo && r.pre({
|
||||
style: {
|
||||
fontSize: '0.75em',
|
||||
},
|
||||
}, [
|
||||
JSON.stringify(this.props.modules, null, 2),
|
||||
]),
|
||||
] : []);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(
|
||||
state => ({
|
||||
modules: state.pulse.infos.modules,
|
||||
preferences: state.preferences,
|
||||
}),
|
||||
dispatch => ({
|
||||
actions: bindActionCreators(pulseActions, dispatch),
|
||||
}),
|
||||
null,
|
||||
{ withRef: true },
|
||||
)(Cards);
|
33
components/top-left-on-screen-button-group/index.js
Normal file
33
components/top-left-on-screen-button-group/index.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
const {
|
||||
map,
|
||||
} = require('ramda');
|
||||
|
||||
const r = require('r-dom');
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
|
||||
const Button = require('../button');
|
||||
|
||||
const TopLeftOnScreenButtonGroup = props => r.div({
|
||||
classSet: {
|
||||
panel: true,
|
||||
'top-left-on-screen-button-group': true,
|
||||
},
|
||||
}, props.preferences.hideOnScreenButtons ? [] : [
|
||||
r(Button, {
|
||||
autoFocus: true,
|
||||
onClick: props.focusCards,
|
||||
}, 'Cards'),
|
||||
|
||||
r(Button, {
|
||||
autoFocus: true,
|
||||
onClick: props.focusNetwork,
|
||||
}, 'Network'),
|
||||
]);
|
||||
|
||||
module.exports = connect(
|
||||
state => ({
|
||||
preferences: state.preferences,
|
||||
}),
|
||||
)(TopLeftOnScreenButtonGroup);
|
27
index.css
27
index.css
|
@ -50,6 +50,13 @@ div[tabindex="-1"]:focus {
|
|||
display: block;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
.label-user-select {
|
||||
user-select: initial;
|
||||
cursor: text;
|
||||
}
|
||||
.label-passive {
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
}
|
||||
|
@ -235,7 +242,7 @@ div[tabindex="-1"]:focus {
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.panel:not(.open) > * {
|
||||
.panel:not(.open) .button {
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
|
@ -248,6 +255,10 @@ div[tabindex="-1"]:focus {
|
|||
border-top: 1px solid var(--borders);
|
||||
}
|
||||
|
||||
.top-left-on-screen-button-group .button {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.ReactModal__Overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -263,6 +274,7 @@ div[tabindex="-1"]:focus {
|
|||
background: var(--themeBgColor);
|
||||
border: 1px solid var(--borders);
|
||||
padding: 1rem;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.view-wrapper .graph .edge-mouse-handler {
|
||||
|
@ -285,6 +297,15 @@ div[tabindex="-1"]:focus {
|
|||
background-position: center;
|
||||
}
|
||||
|
||||
.node-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.node-text > .volume-thumbnail {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.node-name {
|
||||
pointer-events: initial;
|
||||
user-select: none;
|
||||
|
@ -298,6 +319,10 @@ div[tabindex="-1"]:focus {
|
|||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.node-tunnel-info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.volume-thumbnail-ruler-line {
|
||||
stroke-width: 2px;
|
||||
stroke: var(--borders);
|
||||
|
|
12
renderer.js
12
renderer.js
|
@ -9,7 +9,9 @@ const { Provider: ReduxProvider } = require('react-redux');
|
|||
const createStore = require('./store');
|
||||
|
||||
const Graph = require('./components/graph');
|
||||
const TopLeftOnScreenButtonGroup = require('./components/top-left-on-screen-button-group');
|
||||
const Cards = require('./components/cards');
|
||||
const Network = require('./components/network');
|
||||
const Preferences = require('./components/preferences');
|
||||
const Log = require('./components/log');
|
||||
const ServerInfo = require('./components/server-info');
|
||||
|
@ -22,13 +24,21 @@ const theme = require('./utils/theme');
|
|||
const Root = () => r(ReduxProvider, {
|
||||
store: createStore(),
|
||||
}, r(HotKeys, {
|
||||
}, ({ graphRef, cardsRef, preferencesRef, actions: hotKeysActions }) => r(Modals, {
|
||||
}, ({
|
||||
graphRef,
|
||||
cardsRef,
|
||||
networkRef,
|
||||
preferencesRef,
|
||||
actions: hotKeysActions,
|
||||
}) => r(Modals, {
|
||||
}, ({ actions: modalsActions }) => r(MenuProvider, {
|
||||
...modalsActions,
|
||||
...hotKeysActions,
|
||||
}, [
|
||||
r(TopLeftOnScreenButtonGroup, hotKeysActions),
|
||||
r(Graph, { ref: graphRef, ...modalsActions }),
|
||||
r(Cards, { ref: cardsRef }),
|
||||
r(Network, { ref: networkRef, ...modalsActions }),
|
||||
r(Preferences, { ref: preferencesRef }),
|
||||
r(ServerInfo),
|
||||
r(Log),
|
||||
|
|
21
utils/module-args/index.js
Normal file
21
utils/module-args/index.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
const {
|
||||
map,
|
||||
toPairs,
|
||||
} = require('ramda');
|
||||
|
||||
const separators = {
|
||||
'auth-ip-acl': ';',
|
||||
};
|
||||
|
||||
const formatModuleArgs = object => map(([ k, v ]) => {
|
||||
v = [].concat(v);
|
||||
if (k in separators) {
|
||||
v = v.join(separators[k]);
|
||||
} else {
|
||||
v = v.join(',');
|
||||
}
|
||||
return `${k}=${v}`;
|
||||
}, toPairs(object)).join(' ');
|
||||
|
||||
module.exports = { formatModuleArgs };
|
Loading…
Reference in New Issue
Block a user