Gaëtan Renaudeau
7 years ago
36 changed files with 367 additions and 443 deletions
@ -0,0 +1,17 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
// This is a test example for dev testing purpose.
|
||||
|
|
||||
|
import { Observable } from 'rxjs' |
||||
|
import { createCommand, Command } from 'helpers/ipc' |
||||
|
|
||||
|
type Input = void |
||||
|
type Result = void |
||||
|
|
||||
|
const cmd: Command<Input, Result> = createCommand('testCrash', () => |
||||
|
Observable.create(() => { |
||||
|
process.exit(1) |
||||
|
}), |
||||
|
) |
||||
|
|
||||
|
export default cmd |
@ -0,0 +1,13 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
// This is a test example for dev testing purpose.
|
||||
|
|
||||
|
import { interval } from 'rxjs/observable/interval' |
||||
|
import { createCommand, Command } from 'helpers/ipc' |
||||
|
|
||||
|
type Input = number |
||||
|
type Result = number |
||||
|
|
||||
|
const cmd: Command<Input, Result> = createCommand('testInterval', interval) |
||||
|
|
||||
|
export default cmd |
@ -1,36 +0,0 @@ |
|||||
/* eslint-disable no-bitwise */ |
|
||||
|
|
||||
import bitcoin from 'bitcoinjs-lib' |
|
||||
import bs58 from 'bs58' |
|
||||
|
|
||||
export function toHexDigit(number) { |
|
||||
const digits = '0123456789abcdef' |
|
||||
return digits.charAt(number >> 4) + digits.charAt(number & 0x0f) |
|
||||
} |
|
||||
|
|
||||
export function toHexInt(number) { |
|
||||
return ( |
|
||||
toHexDigit((number >> 24) & 0xff) + |
|
||||
toHexDigit((number >> 16) & 0xff) + |
|
||||
toHexDigit((number >> 8) & 0xff) + |
|
||||
toHexDigit(number & 0xff) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export function encodeBase58Check(vchIn) { |
|
||||
// vchIn = parseHexString(vchIn)
|
|
||||
let chksum = bitcoin.crypto.sha256(vchIn) |
|
||||
chksum = bitcoin.crypto.sha256(chksum) |
|
||||
chksum = chksum.slice(0, 4) |
|
||||
const hash = vchIn.concat(Array.from(chksum)) |
|
||||
return bs58.encode(hash) |
|
||||
} |
|
||||
|
|
||||
export function parseHexString(str) { |
|
||||
const result = [] |
|
||||
while (str.length >= 2) { |
|
||||
result.push(parseInt(str.substring(0, 2), 16)) |
|
||||
str = str.substring(2, str.length) |
|
||||
} |
|
||||
return result |
|
||||
} |
|
@ -1,44 +1,58 @@ |
|||||
// @flow
|
// @flow
|
||||
|
import commands from 'commands' |
||||
import objectPath from 'object-path' |
|
||||
import capitalize from 'lodash/capitalize' |
|
||||
|
|
||||
require('../env') |
require('../env') |
||||
require('../init-sentry') |
require('../init-sentry') |
||||
|
|
||||
const { FORK_TYPE } = process.env |
process.title = 'Internal' |
||||
|
|
||||
process.title = `${require('../../package.json').productName} ${capitalize(FORK_TYPE)}` |
|
||||
|
|
||||
function sendEvent(type: string, data: any, options: Object = { kill: true }) { |
|
||||
process.send({ type, data, options }) |
|
||||
} |
|
||||
|
|
||||
// $FlowFixMe
|
const subscriptions = {} |
||||
let handlers = require(`./${FORK_TYPE}`) // eslint-disable-line import/no-dynamic-require
|
|
||||
// handle babel export object syntax
|
|
||||
if (handlers.default) { |
|
||||
handlers = handlers.default |
|
||||
} |
|
||||
|
|
||||
process.on('message', payload => { |
process.on('message', m => { |
||||
if (payload.data && payload.data.requestId) { |
console.log(m) |
||||
const { data, requestId, id } = payload.data |
if (m.type === 'command') { |
||||
// this is the new type of "command" payload!
|
const { data, requestId, id } = m.command |
||||
const cmd = (handlers.commands || []).find(cmd => cmd.id === id) |
const cmd = commands.find(cmd => cmd.id === id) |
||||
if (!cmd) { |
if (!cmd) { |
||||
console.warn(`command ${id} not found`) |
console.warn(`command ${id} not found`) |
||||
} else { |
|
||||
cmd.exec(data, requestId) |
|
||||
} |
|
||||
} else { |
|
||||
// this will be deprecated!
|
|
||||
const { type, data } = payload |
|
||||
const handler = objectPath.get(handlers, type) |
|
||||
if (!handler) { |
|
||||
console.warn(`No handler found for ${type}`) |
|
||||
return |
return |
||||
} |
} |
||||
handler(sendEvent, data) |
subscriptions[requestId] = cmd.impl(data).subscribe({ |
||||
|
next: data => { |
||||
|
process.send({ |
||||
|
type: 'NEXT', |
||||
|
requestId, |
||||
|
data, |
||||
|
}) |
||||
|
}, |
||||
|
complete: () => { |
||||
|
delete subscriptions[requestId] |
||||
|
process.send({ |
||||
|
type: 'COMPLETE', |
||||
|
requestId, |
||||
|
}) |
||||
|
}, |
||||
|
error: error => { |
||||
|
console.warn('Command error:', error) |
||||
|
delete subscriptions[requestId] |
||||
|
process.send({ |
||||
|
type: 'ERROR', |
||||
|
requestId, |
||||
|
data: { |
||||
|
name: error && error.name, |
||||
|
message: error && error.message, |
||||
|
}, |
||||
|
}) |
||||
|
}, |
||||
|
}) |
||||
|
} else if (m.type === 'command-unsubscribe') { |
||||
|
const { requestId } = m |
||||
|
const sub = subscriptions[requestId] |
||||
|
if (sub) { |
||||
|
sub.unsubscribe() |
||||
|
delete subscriptions[requestId] |
||||
|
} |
||||
} |
} |
||||
}) |
}) |
||||
|
|
||||
|
console.log('Internal process is ready!') |
||||
|
@ -1,22 +0,0 @@ |
|||||
// @flow
|
|
||||
import type { Command } from 'helpers/ipc' |
|
||||
|
|
||||
import getMemInfo from 'commands/getMemInfo' |
|
||||
|
|
||||
/** |
|
||||
* Manager |
|
||||
* ------- |
|
||||
* |
|
||||
* xXx |
|
||||
* xXx |
|
||||
* xXx |
|
||||
* xxxXxxx |
|
||||
* xxXxx |
|
||||
* xXx |
|
||||
* xX x Xx |
|
||||
* xX Xx |
|
||||
* xxXXXXXXXxx |
|
||||
* |
|
||||
*/ |
|
||||
|
|
||||
export const commands: Array<Command<any, any>> = [getMemInfo] |
|
@ -1,100 +1,89 @@ |
|||||
// @flow
|
// @flow
|
||||
|
|
||||
import '@babel/polyfill' |
import '@babel/polyfill' |
||||
|
import invariant from 'invariant' |
||||
import { fork } from 'child_process' |
import { fork } from 'child_process' |
||||
import { BrowserWindow, ipcMain, app } from 'electron' |
import { ipcMain, app } from 'electron' |
||||
import objectPath from 'object-path' |
import { ipcMainListenReceiveCommands } from 'helpers/ipc' |
||||
import path from 'path' |
import path from 'path' |
||||
|
|
||||
import setupAutoUpdater, { quitAndInstall } from './autoUpdate' |
import setupAutoUpdater, { quitAndInstall } from './autoUpdate' |
||||
|
|
||||
const { DEV_TOOLS } = process.env |
|
||||
|
|
||||
// sqlite files will be located in the app local data folder
|
// sqlite files will be located in the app local data folder
|
||||
const LEDGER_LIVE_SQLITE_PATH = path.resolve(app.getPath('userData'), 'sqlite') |
const LEDGER_LIVE_SQLITE_PATH = path.resolve(app.getPath('userData'), 'sqlite') |
||||
|
|
||||
const processes = [] |
let internalProcess |
||||
|
|
||||
function cleanProcesses() { |
const killInternalProcess = () => { |
||||
processes.forEach(kill => kill()) |
if (internalProcess) { |
||||
|
console.log('killing internal process...') |
||||
|
internalProcess.kill('SIGINT') |
||||
|
internalProcess = null |
||||
|
} |
||||
} |
} |
||||
|
|
||||
function sendEventToWindow(name, { type, data }) { |
const forkBundlePath = path.resolve(__dirname, `${__DEV__ ? '../../' : './'}dist/internals`) |
||||
const anotherWindow = BrowserWindow.getAllWindows().find(w => w.name === name) |
|
||||
if (anotherWindow) { |
const bootInternalProcess = () => { |
||||
anotherWindow.webContents.send('msg', { type, data }) |
console.log('booting internal process...') |
||||
} |
internalProcess = fork(forkBundlePath, { |
||||
|
env: { LEDGER_LIVE_SQLITE_PATH }, |
||||
|
}) |
||||
|
internalProcess.on('exit', code => { |
||||
|
console.log(`Internal process ended with code ${code}`) |
||||
|
internalProcess = null |
||||
|
}) |
||||
} |
} |
||||
|
|
||||
function onForkChannel(forkType) { |
process.on('exit', () => { |
||||
return (event: any, payload) => { |
killInternalProcess() |
||||
const { type, data } = payload |
}) |
||||
|
|
||||
let compute = fork(path.resolve(__dirname, `${__DEV__ ? '../../' : './'}dist/internals`), { |
ipcMain.on('clean-processes', () => { |
||||
env: { |
killInternalProcess() |
||||
DEV_TOOLS, |
}) |
||||
FORK_TYPE: forkType, |
|
||||
LEDGER_LIVE_SQLITE_PATH, |
|
||||
}, |
|
||||
}) |
|
||||
|
|
||||
const kill = () => { |
ipcMainListenReceiveCommands({ |
||||
if (compute) { |
onUnsubscribe: requestId => { |
||||
compute.kill('SIGINT') |
if (!internalProcess) return |
||||
compute = null |
internalProcess.send({ type: 'command-unsubscribe', requestId }) |
||||
} |
}, |
||||
|
onCommand: (command, notifyCommandEvent) => { |
||||
|
if (!internalProcess) bootInternalProcess() |
||||
|
const p = internalProcess |
||||
|
invariant(p, 'internalProcess not started !?') |
||||
|
|
||||
|
const handleExit = code => { |
||||
|
p.removeListener('message', handleMessage) |
||||
|
p.removeListener('exit', handleExit) |
||||
|
notifyCommandEvent({ |
||||
|
type: 'ERROR', |
||||
|
requestId: command.requestId, |
||||
|
data: { message: `Internal process error (${code})`, name: 'InternalError' }, |
||||
|
}) |
||||
} |
} |
||||
|
|
||||
processes.push(kill) |
const handleMessage = payload => { |
||||
|
if (payload.requestId !== command.requestId) return |
||||
const onMessage = payload => { |
notifyCommandEvent(payload) |
||||
const { type, data, options = {} } = payload |
if (payload.type === 'ERROR' || payload.type === 'COMPLETE') { |
||||
|
p.removeListener('message', handleMessage) |
||||
if (options.window) { |
p.removeListener('exit', handleExit) |
||||
sendEventToWindow(options.window, { type, data }) |
|
||||
} else { |
|
||||
event.sender.send('msg', { type, data }) |
|
||||
} |
|
||||
if (options.kill && compute) { |
|
||||
kill() |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
compute.on('message', onMessage) |
p.on('exit', handleExit) |
||||
compute.send({ type, data }) |
p.on('message', handleMessage) |
||||
|
p.send({ type: 'command', command }) |
||||
process.on('exit', kill) |
}, |
||||
} |
}) |
||||
} |
|
||||
|
|
||||
// Forwards every `type` messages to another process
|
|
||||
ipcMain.on('devices', onForkChannel('devices')) |
|
||||
ipcMain.on('accounts', onForkChannel('accounts')) |
|
||||
ipcMain.on('manager', onForkChannel('manager')) |
|
||||
|
|
||||
ipcMain.on('clean-processes', cleanProcesses) |
|
||||
|
|
||||
const handlers = { |
// TODO move this to "command" pattern
|
||||
updater: { |
ipcMain.on('updater', (event, { type, data }) => { |
||||
|
const handler = { |
||||
init: setupAutoUpdater, |
init: setupAutoUpdater, |
||||
quitAndInstall, |
quitAndInstall, |
||||
}, |
}[type] |
||||
kill: { |
const send = (type: string, data: *) => event.sender.send('updater', { type, data }) |
||||
process: (send, { pid }) => { |
|
||||
try { |
|
||||
process.kill(pid, 'SIGINT') |
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
ipcMain.on('msg', (event: any, payload) => { |
|
||||
const { type, data } = payload |
|
||||
const handler = objectPath.get(handlers, type) |
|
||||
if (!handler) { |
|
||||
console.warn(`No handler found for ${type}`) |
|
||||
return |
|
||||
} |
|
||||
const send = (type: string, data: *) => event.sender.send('msg', { type, data }) |
|
||||
handler(send, data, type) |
handler(send, data, type) |
||||
}) |
}) |
||||
|
@ -1,34 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { ipcRenderer } from 'electron' |
|
||||
|
|
||||
export default function runJob({ |
|
||||
channel, |
|
||||
job, |
|
||||
successResponse, |
|
||||
errorResponse, |
|
||||
data, |
|
||||
}: { |
|
||||
channel: string, |
|
||||
job: string, |
|
||||
successResponse: string, |
|
||||
errorResponse: string, |
|
||||
data?: any, |
|
||||
}): Promise<any> { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
ipcRenderer.send(channel, { type: job, data }) |
|
||||
ipcRenderer.on('msg', handler) |
|
||||
function handler(e, res) { |
|
||||
const { type, data } = res |
|
||||
if (![successResponse, errorResponse].includes(type)) { |
|
||||
return |
|
||||
} |
|
||||
ipcRenderer.removeListener('msg', handler) |
|
||||
if (type === successResponse) { |
|
||||
resolve(data) |
|
||||
} else if (type === errorResponse) { |
|
||||
reject(data) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} |
|
Loading…
Reference in new issue