Skip to content

Commit 1a5c723

Browse files
author
Alan Shaw
committed
feat: config
Adds the commands `config get [Key]` and `config set <Key> <Value>`. The config command uses the context property `onConfigUpdate` (if set) to mutate the context when a config value is updated. License: MIT Signed-off-by: Alan Shaw <[email protected]>
1 parent 0137968 commit 1a5c723

File tree

9 files changed

+141
-42
lines changed

9 files changed

+141
-42
lines changed

Diff for: README.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,25 @@
44
55
Turn IPFS peers in to file sharing friends.
66

7-
## CLI
7+
## Install
8+
9+
```sh
10+
npm install -g ipfs-friends
11+
```
12+
13+
## Usage
14+
15+
### CLI
16+
17+
1. Install IPFS (JS or Go flavour).
18+
2. Start your daemon (with pubsub enabled):
19+
```sh
20+
(js)ipfs dameon --enable-pubsub-experiment
21+
```
22+
3. Start friends:
23+
```sh
24+
friends
25+
```
826

927
```console
1028
# It all starts with friends

Diff for: auto-complete.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
const Commands = require('./commands')
2-
31
exports.withAutoComplete = fn => {
4-
const autoComplete = s => s.includes(' ') ? [] : Object.keys(Commands)
52
return function fnWithAutoComplete (ctx) {
3+
const autoComplete = s => s.includes(' ') ? [] : Object.keys(ctx.commands)
64
ctx.autoComplete = autoComplete
75
return fn.apply(this, arguments)
86
}

Diff for: commands/config.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const Fs = require('fs')
2+
const Path = require('path')
3+
const { promisify } = require('util')
4+
const Os = require('os')
5+
const log = require('debug')('ipfs-friends:config')
6+
const explain = require('explain-error')
7+
8+
const readFile = promisify(Fs.readFile)
9+
const writeFile = promisify(Fs.writeFile)
10+
const DEFAULT_CONFIG_PATH = Path.join(Os.homedir(), '.ipfs-friends')
11+
12+
const SubCommands = {
13+
async set (ctx, key, value) {
14+
log('set', key, value)
15+
16+
const config = await readConfig(ctx.configPath)
17+
config[key] = value
18+
19+
await writeConfig(config, ctx.configPath)
20+
21+
if (ctx.onConfigUpdate) {
22+
return { ctx: await ctx.onConfigUpdate(key, value) }
23+
}
24+
},
25+
26+
async get (ctx, key) {
27+
log('get', key)
28+
const config = await readConfig(ctx.configPath)
29+
const value = key ? config[key] : config
30+
return { out: value }
31+
}
32+
}
33+
34+
async function readConfig (path) {
35+
path = path || DEFAULT_CONFIG_PATH
36+
let config
37+
38+
try {
39+
config = JSON.parse(await readFile(path))
40+
} catch (err) {
41+
log('failed to read config', err)
42+
}
43+
44+
return config || {}
45+
}
46+
47+
async function writeConfig (config, path) {
48+
path = path || DEFAULT_CONFIG_PATH
49+
try {
50+
await writeFile(path, JSON.stringify(config))
51+
} catch (err) {
52+
throw explain(err, 'failed to write config')
53+
}
54+
}
55+
56+
module.exports = async function config (ctx, subCmd, ...args) {
57+
if (!SubCommands[subCmd]) throw new Error(`${subCmd}: subcommand not found`)
58+
return SubCommands[subCmd](ctx, ...args)
59+
}

Diff for: commands/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
exports.add = require('./add')
2+
exports.config = require('./config')
23
exports.dump = require('./dump')
34
exports.exit = require('./exit')
45
exports.help = require('./help')

Diff for: eval.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
const debug = require('debug')('ipfs-friends:eval')
2-
const Commands = require('./commands')
1+
const log = require('debug')('ipfs-friends:eval')
32

43
module.exports.evaluate = async (ctx, cmd, cmdArgs) => {
5-
debug(cmd, cmdArgs)
4+
log(cmd, cmdArgs)
65
cmd = cmd || ''
76
if (!cmd) return
8-
if (!Commands[cmd]) throw new Error(`${cmd}: command not found`)
9-
return Commands[cmd](ctx, ...cmdArgs)
7+
if (!ctx || !ctx.commands || !ctx.commands[cmd]) {
8+
throw new Error(`${cmd}: command not found`)
9+
}
10+
return ctx.commands[cmd](ctx, ...cmdArgs)
1011
}

Diff for: index.js

+36-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const IpfsApi = require('ipfs-api')
21
const Chalk = require('chalk')
32
const FriendDaemon = require('./lib/friends')
43
const repl = require('./repl')
@@ -12,24 +11,40 @@ module.exports = async function (opts) {
1211
}
1312

1413
async function getInitialCtx () {
15-
const ipfs = IpfsApi()
16-
const friendDaemon = new FriendDaemon(ipfs)
17-
18-
friendDaemon
19-
.on('message:share', ({ peerName, peerId, cid, shareName }) => {
20-
console.log(`🎁 ${peerName} shared ${shareName} ${cid}`)
21-
})
22-
.on('message:shared', ({ peerName, peerId, shareName }) => {
23-
console.log(`${peerName} got ${shareName} 🙌`)
24-
})
25-
.on('message:online', ({ peerName }) => {
26-
console.log(`${peerName} is here ✨🎷🐩`)
27-
})
28-
.on('message:offline', ({ peerName }) => {
29-
console.log(`${peerName} went away 👋`)
30-
})
31-
32-
await friendDaemon.start()
33-
34-
return { ipfs, friendDaemon }
14+
const commands = require('./commands')
15+
16+
const createFriendDaemon = async ipfs => {
17+
const friendDaemon = new FriendDaemon(ipfs)
18+
19+
friendDaemon
20+
.on('message:share', ({ peerName, peerId, cid, shareName }) => {
21+
console.log(`🎁 ${peerName} shared ${shareName} ${cid}`)
22+
})
23+
.on('message:shared', ({ peerName, peerId, shareName }) => {
24+
console.log(`${peerName} got ${shareName} 🙌`)
25+
})
26+
.on('message:online', ({ peerName }) => {
27+
console.log(`${peerName} is here ✨🎷🐩`)
28+
})
29+
.on('message:offline', ({ peerName }) => {
30+
console.log(`${peerName} went away 👋`)
31+
})
32+
33+
await friendDaemon.start()
34+
35+
return friendDaemon
36+
}
37+
38+
const onConfigUpdate = async (key, value) => {
39+
if (key === 'apiAddr') {
40+
const ipfs = require('ipfs-api')(value)
41+
const friendDaemon = await createFriendDaemon(ipfs)
42+
return { ipfs, friendDaemon }
43+
}
44+
}
45+
46+
const apiAddr = (await commands.config({}, 'get', 'apiAddr')).out
47+
const ctx = await onConfigUpdate('apiAddr', apiAddr)
48+
49+
return Object.assign(ctx, { commands, onConfigUpdate })
3550
}

Diff for: lib/friends.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ const EventEmitter = require('events')
44
const explain = require('explain-error')
55

66
class FriendDaemon extends EventEmitter {
7-
constructor (ipfs) {
7+
constructor (ipfs, options) {
88
super()
99
this._ipfs = ipfs
1010
this._index = null
1111
this.onMessage = this.onMessage.bind(this)
12+
this._options = options || {}
1213
}
1314

1415
async start () {
1516
const ipfs = this._ipfs
1617
const { id } = await ipfs.id()
17-
const index = await readFriends(ipfs)
18+
const index = await readFriends(ipfs, { dir: this._options.dataDir })
1819
this.peerId = id
1920
this._index = index
2021
const peerIds = Object.keys(index)
@@ -91,7 +92,7 @@ class FriendDaemon extends EventEmitter {
9192
}
9293

9394
this._index[peerId] = name
94-
await writeFriends(this._ipfs, this._index)
95+
await writeFriends(this._ipfs, this._index, { dir: this._options.dataDir })
9596
await this._ipfs.pubsub.subscribe(peerId, this.onMessage)
9697
}
9798

@@ -127,7 +128,7 @@ class FriendDaemon extends EventEmitter {
127128
}
128129

129130
delete index[peerId]
130-
await writeFriends(this._ipfs, index)
131+
await writeFriends(this._ipfs, index, { dir: this._options.dataDir })
131132
await this._ipfs.pubsub.unsubscribe(peerId, this.onMessage)
132133
this._index = index
133134
}
@@ -163,21 +164,27 @@ class FriendDaemon extends EventEmitter {
163164

164165
module.exports = FriendDaemon
165166

166-
async function readFriends (ipfs) {
167+
const DEFAULT_FRIENDS_DIR = '/friends'
168+
169+
async function readFriends (ipfs, options) {
170+
options = options || {}
171+
const dir = options.dir || DEFAULT_FRIENDS_DIR
167172
try {
168-
const index = await ipfs.files.read('/friends/index.json')
173+
const index = await ipfs.files.read(dir + '/index.json')
169174
return JSON.parse(index)
170175
} catch (err) {
171176
log('failed to read /friends/index.json', err)
172177
return {}
173178
}
174179
}
175180

176-
async function writeFriends (ipfs, index) {
181+
async function writeFriends (ipfs, index, options) {
182+
options = options || {}
183+
const dir = options.dir || DEFAULT_FRIENDS_DIR
177184
const data = Buffer.from(JSON.stringify(index))
178185

179186
try {
180-
await ipfs.files.write('/friends/index.json', data, {
187+
await ipfs.files.write(dir + '/index.json', data, {
181188
create: true,
182189
parents: true,
183190
truncate: true

Diff for: print.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const Chalk = require('chalk')
2-
const debug = require('debug')('ipfs-friends:print')
2+
const log = require('debug')('ipfs-friends:print')
33

44
module.exports = async function print (func) {
55
try {
@@ -12,7 +12,7 @@ module.exports = async function print (func) {
1212
}
1313
}
1414
} catch (err) {
15-
debug(err)
15+
log(err)
1616
console.error(`${Chalk.red('✖')} ${err}`)
1717
}
1818
}

Diff for: repl.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const debug = require('debug')('ipfs-friends:repl')
1+
const log = require('debug')('ipfs-friends:repl')
22

33
const { read } = require('./read')
44
const { evaluate } = require('./eval')
@@ -17,7 +17,7 @@ module.exports = async function repl (ctx, opts) {
1717
const { input } = await opts.read(ctx)
1818
let [ cmd, ...cmdArgs ] = input.split(' ').filter(Boolean)
1919

20-
debug(cmd, cmdArgs)
20+
log(cmd, cmdArgs)
2121

2222
await print(async () => {
2323
const res = await opts.evaluate(ctx, cmd, cmdArgs)

0 commit comments

Comments
 (0)