Store settings in lowdb
This commit is contained in:
@@ -1,21 +1,80 @@
|
|||||||
|
import { rendererEvents } from '../../events'
|
||||||
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
|
import {
|
||||||
|
storageStoreEvent,
|
||||||
|
makeStorageResponseEvent,
|
||||||
|
storageLoadEvent,
|
||||||
|
storageClearEvent,
|
||||||
|
makeStorageAcknoledgementEvent,
|
||||||
|
} from '../../events/StorageEvents'
|
||||||
|
|
||||||
export interface StorageIdentifier<Model> {
|
export interface StorageIdentifier<Model> {
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PersistantStorage {
|
export interface PersistantStorage {
|
||||||
store<Model>(identifier: StorageIdentifier<Model>, data: Model): void
|
store<Model>(identifier: StorageIdentifier<Model>, data: Model): Promise<void>
|
||||||
load<Model>(identifier: StorageIdentifier<Model>): Model | undefined
|
load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined>
|
||||||
|
clear(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalStorage implements PersistantStorage {
|
class RemoteStorage implements PersistantStorage {
|
||||||
public store<Model>(identifier: StorageIdentifier<Model>, data: Model) {
|
private timeoutCallback(event: any, callback: any, reject: any) {
|
||||||
localStorage.setItem(identifier.id, JSON.stringify(data))
|
setTimeout(() => {
|
||||||
|
reject('remote storage timeout')
|
||||||
|
rendererEvents.unsubscribe(event, callback)
|
||||||
|
}, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
public load<Model>(identifier: StorageIdentifier<Model>): Model | undefined {
|
private expectAck(transactionId: string): Promise<void> {
|
||||||
const data = localStorage.getItem(identifier.id)
|
const ack = makeStorageAcknoledgementEvent(transactionId)
|
||||||
return data && JSON.parse(data)
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const callback = () => {
|
||||||
|
resolve()
|
||||||
|
rendererEvents.unsubscribe(ack, callback)
|
||||||
|
}
|
||||||
|
rendererEvents.subscribe(ack, callback)
|
||||||
|
this.timeoutCallback(ack, callback, reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public store<Model>(identifier: StorageIdentifier<Model>, data: Model): Promise<void> {
|
||||||
|
const transactionId = v4()
|
||||||
|
const expectation = this.expectAck(transactionId)
|
||||||
|
rendererEvents.emit(storageStoreEvent, { data, transactionId, store: identifier.id })
|
||||||
|
return expectation
|
||||||
|
}
|
||||||
|
|
||||||
|
public load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined> {
|
||||||
|
const transactionId = v4()
|
||||||
|
const responseEvent = makeStorageResponseEvent(transactionId)
|
||||||
|
|
||||||
|
const promise = new Promise<Model>((resolve, reject) => {
|
||||||
|
const callback = (msg: any) => {
|
||||||
|
const data = msg.data && JSON.parse(msg.data)
|
||||||
|
resolve(data)
|
||||||
|
rendererEvents.unsubscribe(responseEvent, callback)
|
||||||
|
}
|
||||||
|
rendererEvents.subscribe(responseEvent, callback)
|
||||||
|
this.timeoutCallback(responseEvent, callback, reject)
|
||||||
|
})
|
||||||
|
|
||||||
|
rendererEvents.emit(storageLoadEvent, {
|
||||||
|
transactionId,
|
||||||
|
store: identifier.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): Promise<void> {
|
||||||
|
const transactionId = v4()
|
||||||
|
const expectation = this.expectAck(transactionId)
|
||||||
|
|
||||||
|
rendererEvents.emit(storageClearEvent, { transactionId })
|
||||||
|
return expectation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new LocalStorage()
|
export default new RemoteStorage()
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ const storedConnectionsIdentifier: StorageIdentifier<{[s: string]: ConnectionOpt
|
|||||||
id: 'ConnectionManager_connections',
|
id: 'ConnectionManager_connections',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadConnectionSettings = () => (dispatch: Dispatch<any>, getState: () => AppState) => {
|
export const loadConnectionSettings = () => async (dispatch: Dispatch<any>, getState: () => AppState) => {
|
||||||
ensureConnectionsHaveBeenInitialized()
|
await ensureConnectionsHaveBeenInitialized()
|
||||||
const connections = persistantStorage.load(storedConnectionsIdentifier)
|
const connections = await persistantStorage.load(storedConnectionsIdentifier)
|
||||||
|
|
||||||
if (!connections) {
|
if (!connections) {
|
||||||
return
|
return
|
||||||
@@ -92,9 +92,8 @@ export const deleteConnection = (connectionId: string) => (dispatch: Dispatch<an
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureConnectionsHaveBeenInitialized() {
|
async function ensureConnectionsHaveBeenInitialized() {
|
||||||
const connections = persistantStorage.load(storedConnectionsIdentifier)
|
const connections = await persistantStorage.load(storedConnectionsIdentifier)
|
||||||
|
|
||||||
const requiresInitialization = !connections
|
const requiresInitialization = !connections
|
||||||
if (requiresInitialization) {
|
if (requiresInitialization) {
|
||||||
const migratedConnection = loadLegacyConnectionOptions()
|
const migratedConnection = loadLegacyConnectionOptions()
|
||||||
|
|||||||
@@ -315,7 +315,6 @@ class ConnectionSettings extends React.Component<Props, State> {
|
|||||||
|
|
||||||
const mqttOptions = toMqttConnection(this.props.connection)
|
const mqttOptions = toMqttConnection(this.props.connection)
|
||||||
if (mqttOptions) {
|
if (mqttOptions) {
|
||||||
console.log(mqttOptions)
|
|
||||||
this.props.actions.connect(mqttOptions, this.props.connection.id)
|
this.props.actions.connect(mqttOptions, this.props.connection.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
41
backend/src/ConfigStorage.ts
Normal file
41
backend/src/ConfigStorage.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import * as FileAsync from 'lowdb/adapters/FileAsync'
|
||||||
|
import * as lowdb from 'lowdb'
|
||||||
|
import { backendEvents } from '../../events'
|
||||||
|
import {
|
||||||
|
makeStorageResponseEvent,
|
||||||
|
storageClearEvent,
|
||||||
|
storageLoadEvent,
|
||||||
|
storageStoreEvent,
|
||||||
|
makeStorageAcknoledgementEvent,
|
||||||
|
} from '../../events/StorageEvents'
|
||||||
|
|
||||||
|
export default class ConfigStorage {
|
||||||
|
private adapter: any
|
||||||
|
constructor(file: string) {
|
||||||
|
this.adapter = new FileAsync(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init() {
|
||||||
|
const database: lowdb.LoDashExplicitAsyncWrapper<any> = await lowdb(this.adapter)
|
||||||
|
backendEvents.subscribe(storageStoreEvent, async (event) => {
|
||||||
|
await database.set(event.store, event.data).write()
|
||||||
|
backendEvents.emit(makeStorageAcknoledgementEvent(event.transactionId), undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
backendEvents.subscribe(storageLoadEvent, async (event) => {
|
||||||
|
const responseEvent = makeStorageResponseEvent(event.transactionId)
|
||||||
|
try {
|
||||||
|
const data = await database.get(event.store).value()
|
||||||
|
backendEvents.emit(responseEvent, { data, transactionId: event.transactionId })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
backendEvents.emit(responseEvent, { transactionId: event.transactionId })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
backendEvents.subscribe(storageClearEvent, async (event) => {
|
||||||
|
await database.drop()
|
||||||
|
backendEvents.emit(makeStorageAcknoledgementEvent(event.transactionId), undefined)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
updateAvailable,
|
updateAvailable,
|
||||||
} from '../../events'
|
} from '../../events'
|
||||||
import { DataSource, MqttSource } from './DataSource'
|
import { DataSource, MqttSource } from './DataSource'
|
||||||
|
import ConfigStorage from './ConfigStorage'
|
||||||
import { UpdateInfo } from 'builder-util-runtime'
|
import { UpdateInfo } from 'builder-util-runtime'
|
||||||
|
|
||||||
export class ConnectionManager {
|
export class ConnectionManager {
|
||||||
@@ -80,3 +80,6 @@ class UpdateNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const updateNotifier = new UpdateNotifier()
|
export const updateNotifier = new UpdateNotifier()
|
||||||
|
|
||||||
|
const configStorage = new ConfigStorage('blah.json')
|
||||||
|
configStorage.init()
|
||||||
|
|||||||
@@ -21,10 +21,11 @@ class IpcMainEventBus implements EventBusInterface {
|
|||||||
this.ipc = ipc
|
this.ipc = ipc
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribe<MessageType>(event: Event<MessageType>, callback:(msg: MessageType) => void) {
|
public subscribe<MessageType>(subscribeEvent: Event<MessageType>, callback:(msg: MessageType) => void) {
|
||||||
console.log('subscribing', event.topic)
|
console.log('subscribing', subscribeEvent.topic)
|
||||||
this.ipc.on(event.topic, (event: any, arg: any) => {
|
this.ipc.on(subscribeEvent.topic, (event: any, arg: any) => {
|
||||||
this.client = event.sender
|
this.client = event.sender
|
||||||
|
console.log(subscribeEvent.topic, arg)
|
||||||
callback(arg)
|
callback(arg)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
38
events/StorageEvents.ts
Normal file
38
events/StorageEvents.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Event } from './'
|
||||||
|
|
||||||
|
interface StorageEvent {
|
||||||
|
transactionId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StoreCommand extends StorageEvent {
|
||||||
|
store: string,
|
||||||
|
data: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadCommand extends StorageEvent {
|
||||||
|
store: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const storageStoreEvent: Event<StoreCommand> = {
|
||||||
|
topic: 'storage/store',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const storageLoadEvent: Event<LoadCommand> = {
|
||||||
|
topic: 'storage/load',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeStorageAcknoledgementEvent(transactionId: string): Event<StoreCommand> {
|
||||||
|
return {
|
||||||
|
topic: `storage/ack/${transactionId}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeStorageResponseEvent(transactionId: string): Event<StoreCommand> {
|
||||||
|
return {
|
||||||
|
topic: `storage/response/${transactionId}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const storageClearEvent: Event<StorageEvent> = {
|
||||||
|
topic: 'storage/clear',
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.1.7",
|
"@types/chai": "^4.1.7",
|
||||||
|
"@types/lowdb": "^1.0.6",
|
||||||
"@types/mime": "^2.0.0",
|
"@types/mime": "^2.0.0",
|
||||||
"@types/mocha": "^5.2.5",
|
"@types/mocha": "^5.2.5",
|
||||||
"@types/mustache": "^0.8.32",
|
"@types/mustache": "^0.8.32",
|
||||||
@@ -75,6 +76,7 @@
|
|||||||
"electron-log": "^2.2.17",
|
"electron-log": "^2.2.17",
|
||||||
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
|
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
|
||||||
"electron-updater": "^4.0.6",
|
"electron-updater": "^4.0.6",
|
||||||
|
"lowdb": "^1.0.0",
|
||||||
"mqtt": "^2.18.8",
|
"mqtt": "^2.18.8",
|
||||||
"sha1": "^1.1.1"
|
"sha1": "^1.1.1"
|
||||||
}
|
}
|
||||||
|
|||||||
34
yarn.lock
34
yarn.lock
@@ -119,6 +119,18 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
|
||||||
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
|
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
|
||||||
|
|
||||||
|
"@types/lodash@*":
|
||||||
|
version "4.14.121"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.121.tgz#9327e20d49b95fc2bf983fc2f045b2c6effc80b9"
|
||||||
|
integrity sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ==
|
||||||
|
|
||||||
|
"@types/lowdb@^1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lowdb/-/lowdb-1.0.6.tgz#0e7adecb87cd79c1e97d50043d9835b65d59023f"
|
||||||
|
integrity sha512-C/p2p3ud6buHPUaj5QTN3gGera9Pi39aCQoQ1ngRZ2hsWeoqok4aCF/Jjj8FDsnSOTaQHrKI92/KHGt6S+Oy+Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/lodash" "*"
|
||||||
|
|
||||||
"@types/mime@^2.0.0":
|
"@types/mime@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
|
||||||
@@ -1589,7 +1601,7 @@ got@^6.7.1:
|
|||||||
unzip-response "^2.0.1"
|
unzip-response "^2.0.1"
|
||||||
url-parse-lax "^1.0.0"
|
url-parse-lax "^1.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6:
|
||||||
version "4.1.15"
|
version "4.1.15"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||||
@@ -2171,7 +2183,7 @@ lodash.zip@^4.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
|
resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
|
||||||
integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=
|
integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=
|
||||||
|
|
||||||
lodash@^4.17.10, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10:
|
lodash@4, lodash@^4.17.10, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10:
|
||||||
version "4.17.11"
|
version "4.17.11"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||||
@@ -2194,6 +2206,17 @@ loud-rejection@^1.0.0:
|
|||||||
currently-unhandled "^0.4.1"
|
currently-unhandled "^0.4.1"
|
||||||
signal-exit "^3.0.0"
|
signal-exit "^3.0.0"
|
||||||
|
|
||||||
|
lowdb@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064"
|
||||||
|
integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.3"
|
||||||
|
is-promise "^2.1.0"
|
||||||
|
lodash "4"
|
||||||
|
pify "^3.0.0"
|
||||||
|
steno "^0.4.1"
|
||||||
|
|
||||||
lowercase-keys@^1.0.0:
|
lowercase-keys@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
@@ -3296,6 +3319,13 @@ stat-mode@^0.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502"
|
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502"
|
||||||
integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI=
|
integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI=
|
||||||
|
|
||||||
|
steno@^0.4.1:
|
||||||
|
version "0.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb"
|
||||||
|
integrity sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.3"
|
||||||
|
|
||||||
stream-shift@^1.0.0:
|
stream-shift@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
||||||
|
|||||||
Reference in New Issue
Block a user