import { AppState } from '../reducers' ; import { clearLegacyConnectionOptions , loadLegacyConnectionOptions } from '../model/LegacyConnectionSettings' ; import { ConnectionOptions , createEmptyConnection , makeDefaultConnections , CertificateParameters , } from '../model/ConnectionOptions' ; import { default as persistentStorage , StorageIdentifier } from '../utils/PersistentStorage' ; import { Dispatch } from 'redux' ; import { showError } from './Global' ; import { remote } from 'electron' ; import { promises as fsPromise } from 'fs' ; import * as path from 'path' ; import { ActionTypes , Action } from '../reducers/ConnectionManager' ; import { Subscription } from '../../../backend/src/DataSource/MqttSource' ; import { connectionsMigrator } from './migrations/Connection' ; export interface ConnectionDictionary { [ s ] : ConnectionOptions ; } const storedConnectionsIdentifier = { id : 'ConnectionManager_connections' , } ; export const loadConnectionSettings = ( ) => async ( dispatch , getState ) => { let connections ; try { await ensureConnectionsHaveBeenInitialized ( ) ; connections = await persistentStorage . load ( storedConnectionsIdentifier ) ; if ( connections && connectionsMigrator . isMigrationNecessary ( connections ) ) { connections = connectionsMigrator . applyMigrations ( connections ) ; await persistentStorage . store ( storedConnectionsIdentifier , connections ) ; } } catch ( error ) { dispatch ( showError ( error ) ) ; } if ( ! connections ) { return ; } dispatch ( setConnections ( connections ) ) ; const firstKey = Object . keys ( connections ) [ 0 ] ; if ( firstKey ) { dispatch ( selectConnection ( firstKey ) ) ; } } ; export type CertificateTypes = 'selfSignedCertificate' | 'clientCertificate' | 'clientKey' ; export const selectCertificate = ( type , connectionId ) => async ( dispatch , getState ) => { try { const certificate = await openCertificate ( ) ; dispatch ( updateConnection ( connectionId , { [ type ] : certificate , } ) ) ; } catch ( error ) { dispatch ( showError ( error ) ) ; } } ; async function openCertificate ( ) { const rejectReasons = { noCertificateSelected : 'No certificate selected' , certificateSizeDoesNotMatch : 'Certificate size larger/smaller then expected.' , } ; const openDialogReturnValue = await remote . dialog . showOpenDialog ( remote . getCurrentWindow ( ) , { properties : [ 'openFile' ] , securityScopedBookmarks : true , } ) ; const selectedFile = openDialogReturnValue . filePaths && openDialogReturnValue . filePaths [ 0 ] ; if ( ! selectedFile ) { throw rejectReasons . noCertificateSelected ; } const data = await fsPromise . readFile ( selectedFile ) ; if ( data . length > 16384 || data . length < 64 ) { throw rejectReasons . certificateSizeDoesNotMatch ; } return { data : data . toString ( 'base64' ) , name : path . basename ( selectedFile ) , } ; } export const saveConnectionSettings = ( ) => async ( dispatch , getState ) => { try { console . log ( 'store settings' ) ; await persistentStorage . store ( storedConnectionsIdentifier , getState ( ) . connectionManager . connections ) ; } catch ( error ) { dispatch ( showError ( error ) ) ; } } ; export const updateConnection = ( connectionId , changeSet ) : Action => ( { connectionId , changeSet , type : ActionTypes . CONNECTION_MANAGER_UPDATE_CONNECTION , } ) ; export const addSubscription = ( subscription , connectionId ) : Action => ( { connectionId , subscription , type : ActionTypes . CONNECTION_MANAGER_ADD_SUBSCRIPTION , } ) ; export const deleteSubscription = ( subscription , connectionId ) : Action => ( { connectionId , subscription , type : ActionTypes . CONNECTION_MANAGER_DELETE_SUBSCRIPTION , } ) ; export const createConnection = ( ) => ( dispatch ) => { const newConnection = createEmptyConnection ( ) ; dispatch ( addConnection ( newConnection ) ) ; dispatch ( selectConnection ( newConnection . id ) ) ; } ; export const setConnections = ( connections ) : Action => ( { connections , type : ActionTypes . CONNECTION_MANAGER_SET_CONNECTIONS , } ) ; export const selectConnection = ( connectionId ) : Action => ( { selected : connectionId , type : ActionTypes . CONNECTION_MANAGER_SELECT_CONNECTION , } ) ; export const addConnection = ( connection ) : Action => ( { connection , type : ActionTypes . CONNECTION_MANAGER_ADD_CONNECTION , } ) ; export const toggleAdvancedSettings = ( ) : Action => ( { type : ActionTypes . CONNECTION_MANAGER_TOGGLE_ADVANCED_SETTINGS , } ) ; export const toggleCertificateSettings = ( ) : Action => ( { type : ActionTypes . CONNECTION_MANAGER_TOGGLE_CERTIFICATE_SETTINGS , } ) ; export const deleteConnection = ( connectionId ) => ( dispatch , getState ) => { const connectionIds = Object . keys ( getState ( ) . connectionManager . connections ) ; const connectionIdLocation = connectionIds . indexOf ( connectionId ) ; const remainingIds = connectionIds . filter ( id => id !== connectionId ) ; const nextSelectedConnectionIndex = Math . min ( remainingIds . length - 1 , connectionIdLocation ) ; const nextSelectedConnection = remainingIds [ nextSelectedConnectionIndex ] ; dispatch ( { connectionId , type : ActionTypes . CONNECTION_MANAGER_DELETE_CONNECTION , } ) ; if ( nextSelectedConnection ) { dispatch ( selectConnection ( nextSelectedConnection ) ) ; } } ; async function ensureConnectionsHaveBeenInitialized ( ) { let connections = await persistentStorage . load ( storedConnectionsIdentifier ) ; const requiresInitialization = ! connections ; if ( requiresInitialization ) { const migratedConnection = loadLegacyConnectionOptions ( ) ; const defaultConnections = makeDefaultConnections ( ) ; connections = { ... migratedConnection , ... defaultConnections , } ; await persistentStorage . store ( storedConnectionsIdentifier , connections ) ; clearLegacyConnectionOptions ( ) ; } }