Skip to content

Commit 1cfbd08

Browse files
dryajovdaviddias
authored andcommitted
feat: Complete revamp! Spawn js/go daemons locally or remote (from the browser) (#176)
1 parent 3c328de commit 1cfbd08

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3678
-863
lines changed

.aegir.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict'
2+
3+
const createServer = require('./src').createServer
4+
5+
const server = createServer()
6+
module.exports = {
7+
karma: {
8+
files: [{
9+
pattern: 'test/fixtures/**/*',
10+
watched: false,
11+
served: true,
12+
included: false
13+
}],
14+
singleRun: true
15+
},
16+
hooks: {
17+
browser: {
18+
pre: server.start.bind(server),
19+
post: server.stop.bind(server)
20+
}
21+
}
22+
}

.appveyor.yml

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1+
version: "{build}"
2+
13
environment:
24
matrix:
35
- nodejs_version: "6"
46
- nodejs_version: "8"
57

6-
# cache:
7-
# - node_modules
8-
9-
platform:
10-
- x64
8+
matrix:
9+
fast_finish: true
1110

1211
install:
13-
- ps: Install-Product node $env:nodejs_version $env:platform
12+
# Install Node.js
13+
- ps: Install-Product node $env:nodejs_version
14+
15+
# Upgrade npm
16+
- npm install -g npm
17+
18+
# Output our current versions for debugging
1419
- node --version
1520
- npm --version
21+
22+
# Install our package dependencies
1623
- npm install
1724

1825
test_script:
19-
- npm test
26+
- npm run test
2027

2128
build: off
22-
23-
version: "{build}"

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ node_modules
3636

3737
dist
3838
docs
39+
40+
.idea

README.md

+212-16
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,33 @@
1010
[![Appveyor CI](https://ci.appveyor.com/api/projects/status/4p9r12ch0jtthnha?svg=true)](https://ci.appveyor.com/project/wubalubadubdub/js-ipfsd-ctl-a9ywu)
1111
[![Dependency Status](https://david-dm.org/ipfs/js-ipfsd-ctl.svg?style=flat-square)](https://david-dm.org/ipfs/js-ipfsd-ctl) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
1212

13-
> Control an ipfs node daemon using Node.js
13+
> Control an IPFS daemon using JavaScript in Node.js or in the Browser.
14+
15+
```
16+
+-----+
17+
| H |
18+
| T |
19+
+-----------------------------+ | T |
20+
| Node.js | +-----------------------+ | P | +-----------------------------+
21+
| | | | | | | BROWSER |
22+
| +-----------------------+ | | IPFS Daemon | | S | | |
23+
| | Local Daemon Ctrl | | | | | E | | +----------------------+ |
24+
| | +------- -------- R -----|---- Remote Daemon Ctrl | |
25+
| +-----------------------+ | +-----|-----------|-----+ | V | | | | |
26+
| | | | | E | | +----------------------+ |
27+
| +-----------------------+ | | | | R | | |
28+
| | IPFS API | | | | +-----+ | +----------------------+ |
29+
| | -------------+ | | | IPFS API | |
30+
| +-----------------------+ | +-----------------------|---- | |
31+
| | | +----------------------+ |
32+
+-----------------------------+ +-----------------------------+
33+
```
1434

1535
## Table of Contents
1636

1737
- [Install](#install)
1838
- [Usage](#usage)
39+
- [API](#api)
1940
- [Contribute](#contribute)
2041
- [License](#license)
2142

@@ -30,35 +51,210 @@ npm install --save ipfsd-ctl
3051

3152
IPFS daemons are already easy to start and stop, but this module is here to do it from JavaScript itself.
3253

54+
### Spawn an IPFS daemon from Node.js
55+
3356
```js
3457
// Start a disposable node, and get access to the api
35-
// print the node id, and kill the temporary daemon
36-
37-
// IPFS_PATH will point to /tmp/ipfs_***** and will be
38-
// cleaned up when the process exits.
58+
// print the node id, and stop the temporary daemon
3959

40-
var ipfsd = require('ipfsd-ctl')
60+
const DaemonFactory = require('ipfsd-ctl')
61+
const df = DaemonFactory.create()
4162

42-
ipfsd.disposableApi(function (err, ipfs) {
43-
ipfs.id(function (err, id) {
63+
df.spawn(function (err, ipfsd) {
64+
if (err) { throw err }
65+
66+
ipfsd.api.id(function (err, id) {
67+
if (err) { throw err }
68+
4469
console.log(id)
45-
process.exit()
70+
ipfsd.stop()
4671
})
4772
})
4873
```
4974

50-
If you need want to use an existing ipfs installation you can set `$IPFS_EXEC=/path/to/ipfs` to ensure it uses that.
75+
### Spawn an IPFS daemon from the Browser using the provided remote endpoint
76+
77+
```js
78+
// Start a remote disposable node, and get access to the api
79+
// print the node id, and stop the temporary daemon
80+
81+
const DaemonFactory = require('ipfsd-ctl')
82+
83+
const port = 9999
84+
const server = DaemonFactory.createServer(port)
85+
const df = DaemonFactory.create({ remote: true, port: port })
86+
87+
server.start((err) => {
88+
if (err) { throw err }
89+
90+
df.spawn((err, ipfsd) => {
91+
if (err) { throw err }
92+
93+
ipfsd.api.id(function (err, id) {
94+
if (err) { throw err }
95+
96+
console.log(id)
97+
ipfsd.stop(server.stop)
98+
})
99+
})
100+
})
101+
```
102+
103+
## Disposable vs non Disposable nodes
104+
105+
`ipfsd-ctl` can create two types of node controllers, `disposable` and `non-disposable`. A disposable node will be created on a temporary repo which will be optionally initialized and started (the default), as well cleaned up on process exit. A non-disposable node on the other hand, requires the user to initialize and start the node, as well as stop and cleanup after wards. Additionally, a non-disposable will allow you to pass a custom repo using the `repoPath` option, if the `repoPath` is not defined, it will use the default repo for the node type (`$HOME/.ipfs` or `$HOME/.jsipfs`). The `repoPath` parameter is ignored for disposable nodes, as there is a risk of deleting a live repo.
106+
107+
## IPFS executables
108+
109+
`ipfsd-ctl` no longer installs go-ipfs nor js-ipfs dependencies, instead it expects them to be provided by the parent project. In order to be able to use both go and js daemons, please make sure that your project includes these two npm packages as dependencies.
110+
111+
- `ipfs` - the js-ipfs implementation
112+
- `go-ipfs-dep` - the packaged go-ipfs implementation
113+
114+
## API
115+
116+
### Daemon Factory Class
117+
118+
#### `DaemonFactory` - `const df = DaemonFactory.create([options])`
119+
120+
`DaemonFactory.create([options])` returns an object that will expose the `df.spawn` method
121+
122+
- `options` - an optional object with the following properties
123+
- `remote` bool - indicates if the factory should spawn local or remote nodes. By default, local nodes are spawned in Node.js and remote nodes are spawned in Browser environments.
124+
- `port` number - the port number to use for the remote factory. It should match the port on which `DaemonFactory.server` was started. Defaults to 9999.
125+
- `type` - the daemon type to create with this factory. See the section bellow for the supported types
126+
- `exec` - path to the desired IPFS executable to spawn, otherwise `ipfsd-ctl` will try to locate the correct one based on the `type`. In the case of `proc` type, exec is required and expects an IPFS coderef.
127+
128+
`ipfsd-ctl` allows spawning different IPFS implementations, such as:
129+
130+
- **`go`** - calling `DaemonFactory.create({type: 'go'})` will spawn a `go-ipfs` daemon.
131+
- **`js`** - calling `DaemonFactory.create({type: 'js'})` will spawn a `js-ipfs` daemon.
132+
- **`proc`** - calling `DaemonFactory.create({type: 'proc', exec: require('ipfs') })` will spawn an `in process js-ipfs node` using the provided code reference that implements the core IPFS API. Note that, `exec` option to `df.spawn()` is required if `type: 'proc'` is used.
133+
134+
#### DaemonFactory endpoint for remote spawning - `const server = `DaemonFactory.createServer([options]) `
135+
136+
`DaemonFactory.createServer` create an instance of the bundled REST API used by the remote controller.
137+
138+
- exposes `start` and `stop` methods to start and stop the http server endpoint.
139+
140+
#### Spawn a new daemon with `df.spawn`
141+
142+
Spawn either a js-ipfs or go-ipfs daemon
143+
144+
`df.spawn([options], callback)`
145+
146+
`options` is an optional object the following properties:
147+
- `init` bool (default true) - should the node be initialized
148+
- `start` bool (default true) - should the node be started
149+
- `repoPath` string - the repository path to use for this node, ignored if node is disposable
150+
- `disposable` bool (default false) - a new repo is created and initialized for each invocation, as well as cleaned up automatically once the process exits
151+
- `args` - array of cmd line arguments to be passed to ipfs daemon
152+
- `config` - ipfs configuration options
153+
154+
`callback` - is a function with the signature `function (err, ipfsd)` where:
155+
- `err` - is the error set if spawning the node is unsuccessful
156+
- `ipfsd` - is the daemon controller instance:
157+
- `api` - a property of `ipfsd`, an instance of [ipfs-api](https://github.com/ipfs/js-ipfs-api) attached to the newly created ipfs node
158+
159+
### IPFS Daemon Controller (`ipfsd`)
160+
161+
The IPFS daemon controller (`ipfsd`) allows you to interact with the spawned IPFS daemon.
162+
163+
#### `ipfsd.apiAddr` (getter)
164+
165+
Get the address (multiaddr) of connected IPFS API. Returns a multiaddr
166+
167+
#### `ipfsd.gatewayAddr` (getter)
51168

52-
For more details see https://ipfs.github.io/js-ipfsd-ctl/.
169+
Get the address (multiaddr) of connected IPFS HTTP Gateway. Returns a multiaddr.
53170

171+
#### `ipfsd.repoPath` (getter)
172+
173+
Get the current repo path. Returns string.
174+
175+
#### `ipfsd.started` (getter)
176+
177+
Is the node started. Returns a boolean.
178+
179+
#### `init([initOpts], callback)`
180+
181+
Initialize a repo.
182+
183+
`initOpts` (optional) is an object with the following properties:
184+
- `keysize` (default 2048) - The bit size of the identity key.
185+
- `directory` (default IPFS_PATH if defined, or ~/.ipfs for go-ipfs and ~/.jsipfs for js-ipfs) - The location of the repo.
186+
187+
`callback` is a function with the signature `function (Error, ipfsd)` where `err` is an Error in case something goes wrong and `ipfsd` is the daemon controller instance.
188+
189+
#### `ipfsd.cleanup(callback)`
190+
191+
Delete the repo that was being used. If the node was marked as `disposable` this will be called automatically when the process is exited.
192+
193+
`callback` is a function with the signature `function(err)`.
194+
195+
#### `ipfsd.start(flags, callback)`
196+
197+
Start the daemon.
198+
199+
`flags` - Flags array to be passed to the `ipfs daemon` command.
200+
201+
`callback` is a function with the signature `function(err, ipfsApi)` that receives an instance of `ipfs-api` on success or an instance of `Error` on failure
202+
203+
204+
#### `ipfsd.stop([callback])`
205+
206+
Stop the daemon.
207+
208+
`callback` is a function with the signature `function(err)` callback - function that receives an instance of `Error` on failure
209+
210+
#### `ipfsd.killProcess([callback])`
211+
212+
Kill the `ipfs daemon` process.
213+
214+
First a `SIGTERM` is sent, after 10.5 seconds `SIGKILL` is sent if the process hasn't exited yet.
215+
216+
`callback` is a function with the signature `function()` called once the process is killed
217+
218+
#### `ipfsd.pid()`
219+
220+
Get the pid of the `ipfs daemon` process. Returns the pid number
221+
222+
#### `ipfsd.getConfig([key], callback)`
223+
224+
Returns the output of an `ipfs config` command. If no `key` is passed, the whole config is returned as an object.
225+
226+
`key` (optional) - A specific config to retrieve.
227+
228+
`callback` is a function with the signature `function(err, (Object|string))` that receives an object or string on success or an `Error` instance on failure
229+
230+
#### `ipfsd.setConfig(key, value, callback)`
231+
232+
Set a config value.
233+
234+
`key` - the key of the config entry to change/set
235+
236+
`value` - the config value to change/set
237+
238+
`callback` is a function with the signature `function(err)` callback - function that receives an `Error` instance on failure
239+
240+
#### `ipfsd.version(callback)`
241+
242+
Get the version of ipfs
243+
244+
`callback` is a function with the signature `function(err, version)`
245+
246+
### IPFS Client (`ipfsd.api`)
247+
248+
An instance of [ipfs-api](https://github.com/ipfs/js-ipfs-api#api) that is used to interact with the daemon.
249+
250+
This instance is returned for each successfully started IPFS daemon, when either `df.spawn({start: true})` (the default) is called, or `ipfsd.start()` is invoked in the case of nodes that were spawned with `df.spawn({start: false})`.
251+
54252
### Packaging
55253

56-
`ipfsd-ctl` can be packaged in Electron applications, but the ipfs binary
57-
has to be excluded from asar (Electron Archives),
254+
`ipfsd-ctl` can be packaged in Electron applications, but the ipfs binary has to be excluded from asar (Electron Archives).
58255
[read more about unpack files from asar](https://electron.atom.io/docs/tutorial/application-packaging/#adding-unpacked-files-in-asar-archive).
59-
`ipfsd-ctl` will try to detect if used from within an `app.asar` archive
60-
and tries to resolve ipfs from `app.asar.unpacked`. The ipfs binary is part of
61-
the `go-ipfs-dep` module.
256+
257+
`ipfsd-ctl` will try to detect if used from within an `app.asar` archive and tries to resolve ipfs from `app.asar.unpacked`. The ipfs binary is part of the `go-ipfs-dep` module.
62258

63259
```bash
64260
electron-packager ./ --asar.unpackDir=node_modules/go-ipfs-dep

circle.yml

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
machine:
33
node:
44
version: stable
5+
6+
test:
7+
post:
8+
- npm run coverage -- --upload --providers coveralls
59

610
dependencies:
711
pre:

examples/disposableApi.js

-18
This file was deleted.

examples/electron-asar/app.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
/* eslint no-console: 0 */
22
'use strict'
33

4-
const { app, ipcMain, BrowserWindow } = require('electron')
5-
const ipfsd = require('ipfsd-ctl')
4+
const electron = require('electron')
5+
const app = electron.app
6+
const ipcMain = electron.ipcMain
7+
const BrowserWindow = electron.BrowserWindow
8+
9+
const DaemonFactory = require('ipfsd-ctl')
10+
const df = DaemonFactory.create()
611

712
app.on('ready', () => {
813
const win = new BrowserWindow({
@@ -15,20 +20,22 @@ ipcMain.on('start', ({ sender }) => {
1520
console.log('starting disposable IPFS')
1621
sender.send('message', 'starting disposable IPFS')
1722

18-
ipfsd.disposableApi((err, ipfs) => {
23+
df.spawn((err, ipfsd) => {
1924
if (err) {
2025
sender.send('error', err)
2126
throw err
2227
}
28+
2329
console.log('get id')
2430
sender.send('message', 'get id')
25-
ipfs.id(function (err, id) {
31+
ipfsd.api.id((err, id) => {
2632
if (err) {
2733
sender.send('error', err)
2834
throw err
2935
}
3036
console.log('got id', id)
3137
sender.send('id', JSON.stringify(id))
38+
ipfsd.stop()
3239
})
3340
})
3441
})

0 commit comments

Comments
 (0)