Files: 8b6827337d6c9a677261c5987cb95bbee685d870 / index.js
4152 bytesRaw
1 | var createConfig = require('ssb-config/inject') |
2 | var ssbKeys = require('ssb-keys') |
3 | var path = require('path') |
4 | var fs = require('fs') |
5 | var MultiServer = require('multiserver') |
6 | var msShs = require('multiserver/plugins/shs') |
7 | var msNet = require('multiserver/plugins/net') |
8 | var muxrpc = require('muxrpc') |
9 | var pull = require('pull-stream') |
10 | var ssbHash = require('pull-hash/ext/ssb') |
11 | var multicb = require('multicb') |
12 | |
13 | function toSodiumKeys(keys) { |
14 | if(!keys || !keys.public) return null |
15 | return { |
16 | publicKey: new Buffer(keys.public.replace('.ed25519', ''), 'base64'), |
17 | secretKey: new Buffer(keys.private.replace('.ed25519', ''), 'base64'), |
18 | } |
19 | } |
20 | |
21 | function fixAddBlob(add) { |
22 | return function (hash, cb) { |
23 | if (typeof hash === 'function') cb = hash, hash = null |
24 | var done = multicb({ pluck: 1, spread: true }) |
25 | var sink = pull( |
26 | ssbHash(done()), |
27 | add(hash, done()) |
28 | ) |
29 | done(cb) |
30 | return sink |
31 | } |
32 | } |
33 | |
34 | function fork(before, after) { |
35 | return function () { |
36 | before.apply(this, arguments) |
37 | after.apply(this, arguments) |
38 | } |
39 | } |
40 | |
41 | module.exports = function (opts, cb) { |
42 | if (typeof opts === 'function') cb = opts, opts = null |
43 | opts = opts || {} |
44 | var config = createConfig(process.env.ssb_appname || opts.appName, opts) |
45 | var appKey = config.appKey || |
46 | (config.caps && config.caps.shs && new Buffer(config.caps.shs, 'base64')) |
47 | if (!appKey) return cb(new Error('missing secret-handshake capability key')) |
48 | var keys = config.keys || (config.keys = |
49 | ssbKeys.loadOrCreateSync(path.join(config.path, 'secret'))) |
50 | config.manifestFile = path.join(config.path, 'manifest.json') |
51 | |
52 | var shs = msShs({ |
53 | keys: toSodiumKeys(keys), |
54 | appKey: appKey, |
55 | auth: function (cb) { cb(null, false) }, |
56 | timeout: config.timers && config.timers.handshake || 5e3 |
57 | }) |
58 | |
59 | var ms = MultiServer([ |
60 | [msNet({}), shs] |
61 | ]) |
62 | |
63 | var keyPublic = keys.public.replace('.ed25519', '') |
64 | var host = config.host || 'localhost' |
65 | var remote = 'net:' + host + ':' + config.port + '~shs:' + keyPublic |
66 | |
67 | ;(function tryConnect(remote) { |
68 | ms.client(remote, function (err, stream) { |
69 | if (err && err.code === 'ECONNREFUSED') { |
70 | return spawnSbot(config, function (err, remote) { |
71 | if (err) return cb(new Error( |
72 | 'unable to connect to or start sbot: ' + err.stack)) |
73 | tryConnect(remote) |
74 | }) |
75 | } |
76 | if (err) return cb(new Error('unable to connect to sbot: ' + err.stack)) |
77 | |
78 | var manifest = config.manifest |
79 | || JSON.parse(fs.readFileSync(config.manifestFile)) |
80 | var sbot = muxrpc(manifest, false)() |
81 | sbot.id = '@'+stream.remote.toString('base64')+'.ed25519' |
82 | if (sbot.blobs && sbot.blobs.add) sbot.blobs.add = fixAddBlob(sbot.blobs.add) |
83 | pull(stream, sbot.createStream(), stream) |
84 | delete config.keys |
85 | var keepalive = setInterval(sbot.whoami, 1e3) |
86 | sbot.close = fork(sbot.close, function () { |
87 | clearInterval(keepalive) |
88 | }) |
89 | cb(null, sbot, config) |
90 | }) |
91 | }(remote)) |
92 | } |
93 | |
94 | function spawnSbot(config, cb) { |
95 | console.error('Starting scuttlebot...') |
96 | function cbOnce(err, addr) { |
97 | if (!cb) return |
98 | cb(err, addr) |
99 | cb = null |
100 | } |
101 | var conf = config.party || {} |
102 | var out = typeof conf.out === 'string' |
103 | ? fs.openSync(path.resolve(config.path, conf.out), 'a') |
104 | : conf.out === false ? 'ignore' : 'inherit' |
105 | var err = typeof conf.err === 'string' |
106 | ? fs.openSync(path.resolve(config.path, conf.err), 'a') |
107 | : conf.err === false ? 'ignore' : conf.err === true ? 'inherit' : out |
108 | var proc = require('child_process') |
109 | var scriptPath = path.join(__dirname, 'server.js') |
110 | var child = proc.spawn(process.execPath, [scriptPath], { |
111 | detached: true, |
112 | stdio: ['ignore', out, err, 'ipc'], |
113 | }) |
114 | child.send(config) |
115 | child.once('message', function (msg) { |
116 | var manifestJSON = JSON.stringify(msg.manifest, null, 2) |
117 | fs.writeFile(config.manifestFile, manifestJSON, function (err) { |
118 | if (err) console.error('warning: unable to write manifest: ' + err.stack) |
119 | child.unref() |
120 | cbOnce(null, msg.address) |
121 | }) |
122 | }) |
123 | child.once('error', function (err) { |
124 | cbOnce(new Error('child sbot error: ' + err.stack)) |
125 | }) |
126 | } |
127 |
Built with git-ssb-web