diff --git a/app/src/components/ConnectionSetup/Connection.tsx b/app/src/components/ConnectionSetup/Connection.tsx index cbbdc6b..1ef4723 100644 --- a/app/src/components/ConnectionSetup/Connection.tsx +++ b/app/src/components/ConnectionSetup/Connection.tsx @@ -1,9 +1,14 @@ import * as React from 'react' -import { Typography, Toolbar, Modal, MenuItem, Button, Grid, Paper, TextField, Switch, FormControlLabel } from '@material-ui/core' +import { + Typography, Toolbar, Modal, + MenuItem, Button, Grid, Paper, + TextField, Switch, FormControlLabel, + CircularProgress, +} from '@material-ui/core' import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles' import Notification from './Notification' import { MqttOptions, DataSourceState } from '../../../../backend/src/DataSource' -import { addMqttConnectionEvent, makeConnectionStateEvent, rendererEvents } from '../../../../events' +import { addMqttConnectionEvent, makeConnectionStateEvent, removeConnection, rendererEvents } from '../../../../events' import sha1 = require('sha1') interface Props { @@ -14,18 +19,20 @@ interface Props { } const protocols = [ - 'tcp://', + 'mqtt://', 'ws://', ] interface State { + connecting: boolean + connectionId?: string error?: Error visible: boolean host: string protocol: string port: number - ssl: boolean - sslValidation: boolean + tls: boolean + certValidation: boolean clientId: string username: string password: string @@ -49,11 +56,13 @@ class Connection extends React.Component { host: 'nodered', protocol: protocols[0], port: 1883, - ssl: false, - sslValidation: true, + tls: false, + certValidation: true, clientId: '', username: '', password: '', + connecting: false, + connectionId: undefined, } this.state = Object.assign({}, defaultState, storedSettings) @@ -71,16 +80,24 @@ class Connection extends React.Component { url, username: this.state.username || undefined, password: this.state.username || undefined, - ssl: this.state.ssl, - sslValidation: this.state.sslValidation, + tls: this.state.tls, + certValidation: this.state.certValidation, } } private connect() { + this.setState({ + connecting: true, + }) + const options = this.optionsFromState() const connectionId = (sha1(Math.random() + JSON.stringify(options)).slice(0, 8)) as string + this.setState({ connectionId }) rendererEvents.emit(addMqttConnectionEvent, { options, id: connectionId }) - rendererEvents.subscribe(makeConnectionStateEvent(connectionId), (state: DataSourceState) => { + const event = makeConnectionStateEvent(connectionId) + + rendererEvents.subscribe(event, (state: DataSourceState) => { + console.log(state) if (state.connected) { this.props.onConnection(connectionId) this.setState({ visible: false }) @@ -91,6 +108,13 @@ class Connection extends React.Component { }) } + private disconnect() { + this.setState({ + connecting: false, + }) + rendererEvents.emit(removeConnection, this.state.connectionId) + } + public static styles: StyleRulesCallback = (theme: Theme) => { return { root: { @@ -130,6 +154,7 @@ class Connection extends React.Component { public render() { const { classes } = this.props + return { console.log('close') }}> @@ -190,13 +215,13 @@ class Connection extends React.Component { margin="normal" /> - +
this.setState({ sslValidation: !this.state.sslValidation })} + checked={this.state.certValidation} + onChange={() => this.setState({ certValidation: !this.state.certValidation })} color="primary" /> )} @@ -205,36 +230,55 @@ class Connection extends React.Component { />
- +
this.setState({ ssl: !this.state.ssl })} + checked={this.state.tls} + onChange={() => this.setState({ tls: !this.state.tls })} color="primary" /> )} - label="Encryption" + label="Encryption (tls)" labelPlacement="bottom" />
- +

- + { this.renderConnectButton() }
} + + private renderConnectButton() { + const { classes } = this.props + + if (this.state.connecting) { + return + } + return + } + + private onClickConnect = () => { + this.connect() + } + + private onClickAbort = () => { + this.disconnect() + } } export default withStyles(Connection.styles, { withTheme: true })(Connection) diff --git a/backend/src/DataSource/MqttSource.ts b/backend/src/DataSource/MqttSource.ts index 9b4360d..0d6cf5c 100644 --- a/backend/src/DataSource/MqttSource.ts +++ b/backend/src/DataSource/MqttSource.ts @@ -1,12 +1,13 @@ import { Client, connect as mqttConnect } from 'mqtt' import { DataSource, DataSourceStateMachine } from './' +import * as Url from 'url' export interface MqttOptions { url: string username?: string password?: string - ssl: boolean - sslValidation: boolean + tls: boolean + certValidation: boolean } export class MqttSource implements DataSource { @@ -22,13 +23,25 @@ export class MqttSource implements DataSource { public connect(options: MqttOptions): DataSourceStateMachine { this.stateMachine.setConnecting() - const client = mqttConnect(options.url, { + + const urlStr = options.tls ? options.url.replace(/^(mqtt|ws):/, '$1s:') : options.url + let url + try { + url = Url.parse(urlStr) + } catch (error) { + this.stateMachine.setError(error) + throw error + } + + const client = mqttConnect(url, { resubscribe: false, + rejectUnauthorized: !options.certValidation, }) this.client = client client.on('error', (error: Error) => { + console.log(error) this.stateMachine.setError(error) }) diff --git a/backend/src/index.ts b/backend/src/index.ts index 1f6509b..dc7a97a 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,4 +1,8 @@ -import { addMqttConnectionEvent, backendEvents, makeConnectionStateEvent, makeConnectionMessageEvent, AddMqttConnection } from '../../events' +import { + addMqttConnectionEvent, backendEvents, + makeConnectionStateEvent, removeConnection, + makeConnectionMessageEvent, AddMqttConnection +} from '../../events' import { MqttSource, DataSource } from './DataSource' class ConnectionManager { @@ -6,17 +10,18 @@ class ConnectionManager { public manageConnections() { backendEvents.subscribe(addMqttConnectionEvent, this.handleConnectionRequest) + backendEvents.subscribe(removeConnection, (connectionId) => this.removeConnection(connectionId)) } private handleConnectionRequest = (event: AddMqttConnection) => { - console.log(event) const connectionId = event.id const options = event.options const connection = new MqttSource() this.connections[connectionId] = connection + const connectionStateEvent = makeConnectionStateEvent(connectionId) connection.stateMachine.onUpdate.subscribe((state) => { - backendEvents.emit(makeConnectionStateEvent(connectionId), state) + backendEvents.emit(connectionStateEvent, state) }) connection.connect(options) @@ -36,7 +41,6 @@ class ConnectionManager { public removeConnection(hash: string) { const connection = this.connections[hash] - connection.stateMachine connection.disconnect() delete this.connections[hash] }