diff --git a/app/background/db/client.js b/app/background/db/client.js
index cf1e97c0a..9432deb03 100644
--- a/app/background/db/client.js
+++ b/app/background/db/client.js
@@ -6,4 +6,5 @@ export const clientStub = (ipcRendererInjector) => makeClient(ipcRendererInjecto
'put',
'get',
'del',
+ 'getUserDir',
]);
diff --git a/app/background/db/service.js b/app/background/db/service.js
index ada56e63d..3b8d5e1c1 100644
--- a/app/background/db/service.js
+++ b/app/background/db/service.js
@@ -51,6 +51,10 @@ export async function iteratePrefix(prefix, cb) {
await iter.each(cb);
}
+export async function getUserDir() {
+ return app.getPath('userData');
+}
+
function ensureDB() {
if (!db) {
throw new Error('db not open');
@@ -65,6 +69,7 @@ const methods = {
get,
del,
iteratePrefix,
+ getUserDir,
};
export async function start(server) {
diff --git a/app/background/node/client.js b/app/background/node/client.js
index d65c8cd64..249b00381 100644
--- a/app/background/node/client.js
+++ b/app/background/node/client.js
@@ -17,5 +17,9 @@ export const clientStub = ipcRendererInjector => makeClient(ipcRendererInjector,
'sendRawAirdrop',
'getFees',
'getAverageBlockTime',
- 'getCoin'
+ 'getCoin',
+ 'setNodeDir',
+ 'setAPIKey',
+ 'getDir',
+ 'getAPIKey',
]);
diff --git a/app/background/node/service.js b/app/background/node/service.js
index 31d73c9d8..7ea2dcde6 100644
--- a/app/background/node/service.js
+++ b/app/background/node/service.js
@@ -17,17 +17,52 @@ const Network = require('hsd/lib/protocol/network');
const MIN_FEE = new BigNumber(0.01);
const DEFAULT_BLOCK_TIME = 10 * 60 * 1000;
+const HSD_PREFIX_DIR_KEY = 'hsdPrefixDir';
+const NODE_API_KEY = 'nodeApiKey';
export class NodeService extends EventEmitter {
constructor() {
super();
+ }
+
+ async getAPIKey() {
+ const apiKey = await get(NODE_API_KEY);
+
+ if (apiKey) return apiKey;
+
+ const newKey = crypto.randomBytes(20).toString('hex');
+ await put(NODE_API_KEY, newKey);
+ return newKey;
+ }
+
+ async getDir() {
+ const hsdPrefixDir = await get(HSD_PREFIX_DIR_KEY);
+
+ if (hsdPrefixDir) {
+ return hsdPrefixDir;
+ }
+
+ const newPath = path.join(app.getPath('userData'), 'hsd_data');
+ await put(HSD_PREFIX_DIR_KEY, newPath);
+ return newPath;
+ }
+
+ async setNodeDir(dir) {
+ if (!fs.existsSync(dir)) {
+ throw new Error(`${dir} does not exist`);
+ }
+
+ await put(HSD_PREFIX_DIR_KEY, dir);
+ }
- this.hsdPrefixDir = path.join(app.getPath('userData'), 'hsd_data');
+ async setAPIKey(apiKey) {
+ await put(NODE_API_KEY, apiKey);
}
async configurePaths() {
- if (!fs.existsSync(this.hsdPrefixDir)) {
- await pify(cb => fs.mkdir(this.hsdPrefixDir, {recursive: true}, cb));
+ const dir = await this.getDir();
+ if (!fs.existsSync(dir)) {
+ await pify(cb => fs.mkdir(dir, {recursive: true}, cb));
}
}
@@ -56,11 +91,10 @@ export class NodeService extends EventEmitter {
}
const network = Network.get(networkName);
- const apiKey = crypto.randomBytes(20).toString('hex');
this.networkName = networkName;
this.network = network;
- this.apiKey = apiKey;
+ this.apiKey = await this.getAPIKey();
this.emit('started', this.networkName, this.network, this.apiKey);
}
@@ -78,6 +112,8 @@ export class NodeService extends EventEmitter {
console.log(`Starting node on ${this.networkName} network.`);
+ const dir = await this.getDir();
+
const hsd = new FullNode({
config: true,
argv: true,
@@ -89,7 +125,7 @@ export class NodeService extends EventEmitter {
workers: false,
network: this.networkName,
loader: require,
- prefix: this.hsdPrefixDir,
+ prefix: dir,
listen: true,
bip37: true,
indexAddress: true,
@@ -173,11 +209,6 @@ export class NodeService extends EventEmitter {
return this._execRPC('generatetoaddress', [numblocks, address]);
}
- async getAPIKey() {
- await this._ensureStarted();
- return this.apiKey;
- }
-
async getInfo() {
await this._ensureStarted();
return this.client.getInfo();
@@ -354,6 +385,9 @@ const methods = {
getFees: () => service.getFees(),
getAverageBlockTime: () => service.getAverageBlockTime(),
getCoin: (hash, index) => service.getCoin(hash, index),
+ setNodeDir: data => service.setNodeDir(data),
+ setAPIKey: data => service.setAPIKey(data),
+ getDir: () => service.getDir(),
};
export async function start(server) {
diff --git a/app/components/Modal/MiniModal.js b/app/components/Modal/MiniModal.js
index 98bcf6344..ddd85a35c 100644
--- a/app/components/Modal/MiniModal.js
+++ b/app/components/Modal/MiniModal.js
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import './mini-modal.scss';
+@withRouter
export class MiniModal extends Component {
static propTypes = {
closeRoute: PropTypes.string,
diff --git a/app/pages/Settings/ChangeDirectoryModal.js b/app/pages/Settings/ChangeDirectoryModal.js
new file mode 100644
index 000000000..301e05a46
--- /dev/null
+++ b/app/pages/Settings/ChangeDirectoryModal.js
@@ -0,0 +1,125 @@
+import React, { Component } from "react";
+import {MiniModal} from "../../components/Modal/MiniModal";
+import {clientStub} from "../../background/node/client";
+import c from "classnames";
+import {connect} from "react-redux";
+import {withRouter} from "react-router-dom";
+import Alert from "../../components/Alert";
+import dbClient from "../../utils/dbClient";
+const nodeClient = clientStub(() => require('electron').ipcRenderer);
+const {dialog} = require('electron').remote;
+
+
+@withRouter
+@connect(
+ null,
+ null,
+)
+export default class ChangeDirectoryModal extends Component {
+ state = {
+ directory: '',
+ errorMessage: '',
+ saving: false,
+ };
+
+ async componentDidMount() {
+ const directory = await nodeClient.getDir();
+ const userDir = await dbClient.getUserDir();
+ this.setState({
+ originalDirectory: directory,
+ directory,
+ userDir,
+ });
+ }
+
+ reset = async () => {
+ const {originalDirectory} = this.state;
+ this.setState({
+ originalDirectory,
+ directory: originalDirectory,
+ });
+ };
+
+ saveDir = async () => {
+ const {directory} = this.state;
+ this.setState({ saving: true });
+ try {
+ await nodeClient.setNodeDir(directory);
+ } catch (e) {
+ this.setState({
+ errorMessage: e.message,
+ });
+ }
+
+ this.setState({
+ saving: false,
+ directory,
+ originalDirectory: directory,
+ });
+ };
+
+ pickDirectory = async () => {
+ let savePath = dialog.showOpenDialogSync({
+ properties: ["openDirectory", "promptToCreate", "createDirectory"],
+ });
+
+ this.setState({ directory: savePath[0] });
+ };
+
+ render() {
+ const { errorMessage, directory, originalDirectory, userDir, saving } = this.state;
+
+ return (
+