Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 2f11def

Browse files
committed
feat: add mfs implementation
1 parent c01a83a commit 2f11def

File tree

26 files changed

+502
-192
lines changed

26 files changed

+502
-192
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ You can check the development status at the [Waffle Board](https://waffle.io/ipf
5757
- [Use in Node.js](#use-in-nodejs)
5858
- [Through command line tool](#through-command-line-tool)
5959
- [Use in the browser](#use-in-the-browser)
60+
- [Use with Web Workers](#use-with-web-workers)
6061
- [Usage](#usage)
6162
- [IPFS CLI](#ipfs-cli)
6263
- [IPFS Daemon](#ipfs-daemon)
@@ -135,6 +136,20 @@ You can also load it using a `<script>` using the [unpkg](https://unpkg.com) CDN
135136
```
136137
Inserting one of the above lines will make an `Ipfs` object available in the global namespace.
137138

139+
### Use with Web Workers
140+
141+
Some of the `ipfs.files.*` [MFS](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#mutable-file-system) commands mutate the CID that corresponds to the current root of the file system. A read/write lock on the main thread prevents these mutations from conflicting. With single-process and [Node.js cluster](https://nodejs.org/api/cluster.html) based apps this is completely transparent but [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) require a little extra setup. Use the [`observable-webworkers`](https://www.npmjs.com/package/observable-webworkers) module to allow communication with any Web Workers your application creates:
142+
143+
```javascript
144+
const observe = require('observable-webworkers')
145+
146+
const worker = new Worker('my-worker-script.js')
147+
148+
observe(worker)
149+
```
150+
151+
`js-ipfs` will send extra messages to your Web Workers to orchestrate the read/write locks so please be aware of this in your Worker code.
152+
138153
## Usage
139154

140155
### IPFS CLI

examples/browser-script-tag/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ <h2>Some suggestions</h2>
3030
<code style="display:block; white-space:pre-wrap; background-color:#d7d6d6">
3131
node.files.add(new node.types.Buffer('Hello world!'), (err, filesAdded) => {
3232
if (err) {
33-
return console.error('Error - ipfs files add', err, res)
33+
return console.error('Error - ipfs add', err, res)
3434
}
3535

3636
filesAdded.forEach((file) => console.log('successfully stored', file.hash))

examples/circuit-relaying/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"ipfs-pubsub-room": "~0.3.0"
1919
},
2020
"devDependencies": {
21-
"aegir": "^13.0.5",
21+
"aegir": "^14.0.0",
2222
"http-server": "~0.10.0",
2323
"ipfs-css": "~0.2.0",
2424
"parcel-bundler": "^1.6.2",

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"chai": "^4.1.2",
6767
"delay": "^2.0.0",
6868
"detect-node": "^2.0.3",
69+
"detect-webworker": "^1.0.0",
6970
"dir-compare": "^1.4.0",
7071
"dirty-chai": "^2.0.1",
7172
"eslint-plugin-react": "^7.7.0",
@@ -107,11 +108,12 @@
107108
"hoek": "^5.0.3",
108109
"human-to-milliseconds": "^1.0.0",
109110
"interface-datastore": "~0.4.1",
110-
"ipfs-api": "^22.1.1",
111+
"ipfs-api": "^22.2.1",
111112
"ipfs-bitswap": "~0.20.0",
112113
"ipfs-block": "~0.7.1",
113114
"ipfs-block-service": "~0.14.0",
114115
"ipfs-http-response": "~0.1.2",
116+
"ipfs-mfs": "~0.0.13",
115117
"ipfs-multipart": "~0.1.0",
116118
"ipfs-repo": "~0.22.1",
117119
"ipfs-unixfs": "~0.1.15",
@@ -170,7 +172,8 @@
170172
"through2": "^2.0.3",
171173
"update-notifier": "^2.5.0",
172174
"yargs": "^11.0.0",
173-
"yargs-parser": "^10.0.0"
175+
"yargs-parser": "^10.0.0",
176+
"yargs-promise": "^1.1.0"
174177
},
175178
"optionalDependencies": {
176179
"prom-client": "^11.0.0",

src/cli/bin.js

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
'use strict'
44

5+
const YargsPromise = require('yargs-promise')
56
const yargs = require('yargs')
67
const updateNotifier = require('update-notifier')
78
const readPkgUp = require('read-pkg-up')
8-
const fs = require('fs')
9-
const path = require('path')
109
const utils = require('./utils')
1110
const print = utils.print
11+
const mfs = require('ipfs-mfs/cli')
12+
const debug = require('debug')('ipfs:cli')
1213

1314
const pkg = readPkgUp.sync({cwd: __dirname}).pkg
1415
updateNotifier({
@@ -18,10 +19,6 @@ updateNotifier({
1819

1920
const args = process.argv.slice(2)
2021

21-
// Determine if the first argument is a sub-system command
22-
const commandNames = fs.readdirSync(path.join(__dirname, 'commands'))
23-
const isCommand = commandNames.includes(`${args[0]}.js`)
24-
2522
const cli = yargs
2623
.option('silent', {
2724
desc: 'Write no output',
@@ -34,14 +31,6 @@ const cli = yargs
3431
type: 'string',
3532
default: ''
3633
})
37-
.commandDir('commands', {
38-
// Only include the commands for the sub-system we're using, or include all
39-
// if no sub-system command has been passed.
40-
include (path, filename) {
41-
if (!isCommand) return true
42-
return `${args[0]}.js` === filename
43-
}
44-
})
4534
.epilog(utils.ipfsPathHelp)
4635
.demandCommand(1)
4736
.fail((msg, err, yargs) => {
@@ -56,27 +45,15 @@ const cli = yargs
5645
yargs.showHelp()
5746
})
5847

59-
// If not a sub-system command then load the top level aliases
60-
if (!isCommand) {
61-
// NOTE: This creates an alias of
62-
// `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`.
63-
// This will stay until https://github.com/ipfs/specs/issues/98 is resolved.
64-
const addCmd = require('./commands/files/add')
65-
const catCmd = require('./commands/files/cat')
66-
const getCmd = require('./commands/files/get')
67-
const aliases = [addCmd, catCmd, getCmd]
68-
aliases.forEach((alias) => {
69-
cli.command(alias.command, alias.describe, alias.builder, alias.handler)
70-
})
71-
}
72-
7348
// Need to skip to avoid locking as these commands
7449
// don't require a daemon
7550
if (args[0] === 'daemon' || args[0] === 'init') {
7651
cli
7752
.help()
7853
.strict()
7954
.completion()
55+
.command(require('./commands/daemon'))
56+
.command(require('./commands/init'))
8057
.parse(args)
8158
} else {
8259
// here we have to make a separate yargs instance with
@@ -86,20 +63,58 @@ if (args[0] === 'daemon' || args[0] === 'init') {
8663
if (err) {
8764
throw err
8865
}
66+
8967
utils.getIPFS(argv, (err, ipfs, cleanup) => {
90-
if (err) { throw err }
68+
if (err) {
69+
throw err
70+
}
71+
72+
// add mfs commands
73+
mfs(cli)
74+
75+
// NOTE: This creates an alias of
76+
// `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`.
77+
// This will stay until https://github.com/ipfs/specs/issues/98 is resolved.
78+
const addCmd = require('./commands/files/add')
79+
const catCmd = require('./commands/files/cat')
80+
const getCmd = require('./commands/files/get')
81+
const aliases = [addCmd, catCmd, getCmd]
82+
aliases.forEach((alias) => {
83+
cli.command(alias)
84+
})
9185

9286
cli
87+
.commandDir('commands')
9388
.help()
9489
.strict()
9590
.completion()
96-
.parse(args, { ipfs: ipfs }, (err, argv, output) => {
97-
if (output) { print(output) }
9891

99-
cleanup(() => {
100-
if (err) { throw err }
101-
})
92+
let exitCode = 0
93+
94+
const parser = new YargsPromise(cli, { ipfs })
95+
parser.parse(args)
96+
.then(({ data, argv }) => {
97+
if (data) {
98+
print(data)
99+
}
100+
})
101+
.catch((arg) => {
102+
debug(arg)
103+
104+
// the argument can have a different shape depending on where the error came from
105+
if (arg.message) {
106+
print(arg.message)
107+
} else if (arg.error && arg.error.message) {
108+
print(arg.error.message)
109+
} else {
110+
print('Unknown error, please re-run the command with DEBUG=ipfs:cli to see debug output')
111+
}
112+
113+
exitCode = 1
102114
})
115+
.then(() => cleanup())
116+
.catch(() => {})
117+
.then(() => process.exit(exitCode))
103118
})
104119
})
105120
}

src/cli/commands/files.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/cli/commands/files/get.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ function fileHandler (dir) {
3030
callback(err)
3131
} else {
3232
const fullFilePath = path.join(dir, file.path)
33+
3334
if (file.content) {
3435
file.content
3536
.pipe(fs.createWriteStream(fullFilePath))

src/cli/utils.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const log = debug('cli')
99
log.error = debug('cli:error')
1010
const Progress = require('progress')
1111
const byteman = require('byteman')
12+
const promisify = require('promisify-es6')
1213

1314
exports = module.exports
1415

@@ -40,7 +41,7 @@ function getAPICtl (apiAddr) {
4041

4142
exports.getIPFS = (argv, callback) => {
4243
if (argv.api || isDaemonOn()) {
43-
return callback(null, getAPICtl(argv.api), (cb) => cb())
44+
return callback(null, getAPICtl(argv.api), promisify((cb) => cb()))
4445
}
4546

4647
// Required inline to reduce startup time
@@ -55,13 +56,13 @@ exports.getIPFS = (argv, callback) => {
5556
}
5657
})
5758

58-
const cleanup = (cb) => {
59+
const cleanup = promisify((cb) => {
5960
if (node && node._repo && !node._repo.closed) {
60-
node._repo.close(() => cb())
61+
node._repo.close((err) => cb(err))
6162
} else {
6263
cb()
6364
}
64-
}
65+
})
6566

6667
node.on('error', (err) => {
6768
throw err

src/core/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ exports.dht = require('./dht')
2626
exports.dns = require('./dns')
2727
exports.key = require('./key')
2828
exports.stats = require('./stats')
29+
exports.mfs = require('ipfs-mfs/core')

src/core/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ class IPFS extends EventEmitter {
133133
isIPFS: isIPFS
134134
}
135135

136+
// ipfs.files
137+
const mfs = components.mfs(this)
138+
139+
Object.keys(mfs).forEach(key => {
140+
this.files[key] = mfs[key]
141+
})
142+
136143
boot(this)
137144
}
138145
}

src/http/api/resources/files.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ exports.parseKey = (request, reply) => {
3434
if (!request.query.arg) {
3535
return reply({
3636
Message: "Argument 'key' is required",
37-
Code: 0
37+
Code: 0,
38+
Type: 'error'
3839
}).code(400).takeover()
3940
}
4041

@@ -54,7 +55,8 @@ exports.parseKey = (request, reply) => {
5455
log.error(err)
5556
return reply({
5657
Message: 'invalid ipfs ref path',
57-
Code: 0
58+
Code: 0,
59+
Type: 'error'
5860
}).code(500).takeover()
5961
}
6062

@@ -81,9 +83,9 @@ exports.cat = {
8183
if (err) {
8284
log.error(err)
8385
if (err.message === 'No such file') {
84-
reply({Message: 'No such file'}).code(500)
86+
reply({Message: 'No such file', Code: 0, Type: 'error'}).code(500)
8587
} else {
86-
reply({Message: 'Failed to cat file: ' + err, Code: 0}).code(500)
88+
reply({Message: 'Failed to cat file: ' + err, Code: 0, Type: 'error'}).code(500)
8789
}
8890
return
8991
}
@@ -177,7 +179,8 @@ exports.add = {
177179
if (!request.payload) {
178180
return reply({
179181
Message: 'Array, Buffer, or String is required.',
180-
code: 0
182+
Code: 0,
183+
Type: 'error'
181184
}).code(400).takeover()
182185
}
183186

@@ -211,7 +214,8 @@ exports.add = {
211214
if (!filesParsed) {
212215
return reply({
213216
Message: "File argument 'data' is required.",
214-
code: 0
217+
Code: 0,
218+
Type: 'error'
215219
}).code(400).takeover()
216220
}
217221
fileAdder.end()
@@ -302,7 +306,8 @@ exports.immutableLs = {
302306
if (err) {
303307
return reply({
304308
Message: 'Failed to list dir: ' + err.message,
305-
Code: 0
309+
Code: 0,
310+
Type: 'error'
306311
}).code(500).takeover()
307312
}
308313

src/http/api/routes/files.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const resources = require('./../resources')
4+
const mfs = require('ipfs-mfs/http')
45

56
module.exports = (server) => {
67
const api = server.select('API')
@@ -54,4 +55,6 @@ module.exports = (server) => {
5455
handler: resources.files.immutableLs.handler
5556
}
5657
})
58+
59+
mfs(api)
5760
}

0 commit comments

Comments
 (0)