Commit e5d0b7d8bace9ec48ab12ec7a1a40872a050acad
merge multiserver
Dominic Tarr committed on 10/2/2016, 12:01:36 PMParent: f01447184bc4318a21262c0bed6a7a726e6e3bfe
Parent: 682c3a5400995ac0d881d88c82a8647730578ba2
Files changed
bin.js | changed |
lib/util.js | changed |
package.json | changed |
plugins/gossip/index.js | changed |
plugins/gossip/init.js | changed |
plugins/gossip/schedule.js | changed |
plugins/invite.js | changed |
plugins/replicate.js | changed |
plugins/plugins.js | deleted |
test/invite.js | changed |
test/master.js | changed |
test/server.js | changed |
test/util.js | changed |
test/plugins.js | deleted |
bin.js | ||
---|---|---|
@@ -34,9 +34,8 @@ | ||
34 | 34 … | // special server command: |
35 | 35 … | // import sbot and start the server |
36 | 36 … | |
37 | 37 … | var createSbot = require('./') |
38 | - .use(require('./plugins/plugins')) | |
39 | 38 … | .use(require('./plugins/master')) |
40 | 39 … | .use(require('./plugins/gossip')) |
41 | 40 … | .use(require('./plugins/friends')) |
42 | 41 … | .use(require('./plugins/replicate')) |
@@ -46,11 +45,8 @@ | ||
46 | 45 … | .use(require('./plugins/local')) |
47 | 46 … | .use(require('./plugins/logging')) |
48 | 47 … | .use(require('./plugins/private')) |
49 | 48 … | |
50 | - // add third-party plugins | |
51 | - require('./plugins/plugins').loadUserPlugins(createSbot, config) | |
52 | - | |
53 | 49 … | // start server |
54 | 50 … | |
55 | 51 … | config.keys = keys |
56 | 52 … | var server = createSbot(config) |
@@ -146,4 +142,6 @@ | ||
146 | 142 … | |
147 | 143 … | |
148 | 144 … | |
149 | 145 … | |
146 … | + | |
147 … | + |
lib/util.js | ||
---|---|---|
@@ -65,37 +65,5 @@ | ||
65 | 65 … | a[k] = b |
66 | 66 … | } |
67 | 67 … | } |
68 | 68 … | |
69 | -exports.stringifyAddress = function (e) { | |
70 | - if(isString(e)) return e | |
71 | - return [e.host, e.port || DEFAULT_PORT, e.key].join(':') | |
72 | -} | |
73 | 69 … | |
74 | -exports.parseAddress = function (e) { | |
75 | - if(isString(e)) { | |
76 | - var parts = e.split(':') | |
77 | - var id = parts.pop(), port = parts.pop(), host = parts.join(':') | |
78 | - var e = { | |
79 | - host: host, | |
80 | - port: +(port || DEFAULT_PORT), | |
81 | - key: id | |
82 | - } | |
83 | - return e | |
84 | - } | |
85 | - return e | |
86 | -} | |
87 | - | |
88 | -exports.isAddress = function (addr) { | |
89 | - return ( | |
90 | - ref.isFeedId(addr.key || addr.link) | |
91 | - && isInteger(addr.port) | |
92 | - && typeof addr.host === 'string' && !!addr.host | |
93 | - ) | |
94 | -} | |
95 | - | |
96 | -exports.toAddress = function (e) { | |
97 | - e = exports.parseAddress(e) | |
98 | - e.port = e.port || DEFAULT_PORT | |
99 | - e.host = e.host || 'localhost' | |
100 | - return e | |
101 | -} |
package.json | ||
---|---|---|
@@ -14,26 +14,20 @@ | ||
14 | 14 … | "explain-error": "~1.0.1", |
15 | 15 … | "graphmitter": "^1.6.3", |
16 | 16 … | "ip": "^0.3.3", |
17 | 17 … | "level-memview": "~0.0.0", |
18 | - "map-merge": "~1.1.0", | |
19 | 18 … | "mdmanifest": "^1.0.4", |
20 | 19 … | "minimist": "^1.1.3", |
21 | 20 … | "mkdirp": "~0.5.0", |
22 | 21 … | "multiblob": "^1.10.1", |
23 | 22 … | "multicb": "^1.0.0", |
24 | 23 … | "muxrpc": "^6.1.1", |
25 | 24 … | "muxrpc-validation": "^2.0.0", |
26 | 25 … | "muxrpcli": "^1.0.0", |
27 | - "mv": "^2.1.1", | |
28 | - "mynosql-query": "~1.0.0", | |
29 | - "nomnom": "1.8.0", | |
30 | 26 … | "non-private-ip": "~1.1.0", |
31 | - "observ": "~0.2.0", | |
32 | 27 … | "observ-debounce": "^1.1.1", |
33 | 28 … | "on-change-network": "0.0.2", |
34 | 29 … | "on-wakeup": "^1.0.0", |
35 | - "osenv": "~0.1.0", | |
36 | 30 … | "pull-abortable": "~4.0.0", |
37 | 31 … | "pull-cat": "~1.1.5", |
38 | 32 … | "pull-file": "^0.5.0", |
39 | 33 … | "pull-inactivity": "~2.1.1", |
@@ -46,17 +40,17 @@ | ||
46 | 40 … | "pull-stream": "^3.0.0", |
47 | 41 … | "pull-stream-to-stream": "~1.3.0", |
48 | 42 … | "pull-stringify": "~1.2.2", |
49 | 43 … | "rimraf": "^2.4.2", |
50 | - "secret-stack": "^2.5.0", | |
44 … | + "secret-stack": "^2.5.2", | |
51 | 45 … | "secure-scuttlebutt": "^15.4.1", |
52 | 46 … | "ssb-blobs": "^0.1.6", |
53 | 47 … | "ssb-client": "^4.0.2", |
54 | 48 … | "ssb-config": "^2.0.0", |
55 | 49 … | "ssb-feed": "^2.2.0", |
56 | 50 … | "ssb-keys": "^5.1.2", |
57 | 51 … | "ssb-msgs": "~5.0.0", |
58 | - "ssb-ref": "^2.3.2", | |
52 … | + "ssb-ref": "^2.6.0", | |
59 | 53 … | "statistics": "^2.0.1", |
60 | 54 … | "stream-to-pull-stream": "^1.6.10", |
61 | 55 … | "zerr": "^1.0.0" |
62 | 56 … | }, |
plugins/gossip/index.js | ||
---|---|---|
@@ -5,8 +5,9 @@ | ||
5 | 5 … | var mdm = require('mdmanifest') |
6 | 6 … | var valid = require('../../lib/validators') |
7 | 7 … | var apidoc = require('../../lib/apidocs').gossip |
8 | 8 … | var u = require('../../lib/util') |
9 … | +var ref = require('ssb-ref') | |
9 | 10 … | var ping = require('pull-ping') |
10 | 11 … | var Stats = require('statistics') |
11 | 12 … | var isArray = Array.isArray |
12 | 13 … | var Schedule = require('./schedule') |
@@ -15,8 +16,12 @@ | ||
15 | 16 … | function isFunction (f) { |
16 | 17 … | return 'function' === typeof f |
17 | 18 … | } |
18 | 19 … | |
20 … | +function stringify(peer) { | |
21 … | + return [peer.host, peer.port, peer.key].join(':') | |
22 … | +} | |
23 … | + | |
19 | 24 … | /* |
20 | 25 … | Peers : [{ |
21 | 26 … | key: id, |
22 | 27 … | host: ip, |
@@ -37,9 +42,9 @@ | ||
37 | 42 … | }, |
38 | 43 … | init: function (server, config) { |
39 | 44 … | var notify = Notify() |
40 | 45 … | var conf = config.gossip || {} |
41 | - var home = u.toAddress(server.getAddress()) | |
46 … | + var home = ref.parseAddress(server.getAddress()) | |
42 | 47 … | |
43 | 48 … | //Known Peers |
44 | 49 … | var peers = [] |
45 | 50 … | |
@@ -56,9 +61,9 @@ | ||
56 | 61 … | peers: function () { |
57 | 62 … | return peers |
58 | 63 … | }, |
59 | 64 … | get: function (addr) { |
60 | - addr = u.toAddress(addr) | |
65 … | + addr = ref.parseAddress(addr) | |
61 | 66 … | return u.find(peers, function (a) { |
62 | 67 … | return ( |
63 | 68 … | addr.port === a.port |
64 | 69 … | && addr.host === a.host |
@@ -66,9 +71,9 @@ | ||
66 | 71 … | ) |
67 | 72 … | }) |
68 | 73 … | }, |
69 | 74 … | connect: valid.async(function (addr, cb) { |
70 | - addr = u.toAddress(addr) | |
75 … | + addr = ref.parseAddress(addr) | |
71 | 76 … | if (!addr || typeof addr != 'object') |
72 | 77 … | return cb(new Error('first param must be an address')) |
73 | 78 … | |
74 | 79 … | if(!addr.key) return cb(new Error('address must have ed25519 key')) |
@@ -114,10 +119,10 @@ | ||
114 | 119 … | return notify.listen() |
115 | 120 … | }, |
116 | 121 … | //add an address to the peer table. |
117 | 122 … | add: valid.sync(function (addr, source) { |
118 | - addr = u.toAddress(addr) | |
119 | - if(!u.isAddress(addr)) | |
123 … | + addr = ref.parseAddress(addr) | |
124 … | + if(!ref.isAddress(addr)) | |
120 | 125 … | throw new Error('not a valid address:' + JSON.stringify(addr)) |
121 | 126 … | // check that this is a valid address, and not pointing at self. |
122 | 127 … | |
123 | 128 … | if(addr.key === home.key) return |
@@ -164,9 +169,9 @@ | ||
164 | 169 … | var peer = getPeer(rpc.id) |
165 | 170 … | //don't track clients that connect, but arn't considered peers. |
166 | 171 … | //maybe we should though? |
167 | 172 … | if(!peer) return |
168 | - console.log('+connected', u.stringifyAddress(peer)) | |
173 … | + console.log('Connected', stringify(peer)) | |
169 | 174 … | //means that we have created this connection, not received it. |
170 | 175 … | peer.client = !!isClient |
171 | 176 … | peer.state = 'connected' |
172 | 177 … | peer.stateChange = Date.now() |
@@ -188,9 +193,9 @@ | ||
188 | 193 … | ) |
189 | 194 … | } |
190 | 195 … | |
191 | 196 … | rpc.on('closed', function () { |
192 | - console.log('-disconnected', u.stringifyAddress(peer)) | |
197 … | + console.log('Disconnected', stringify(peer)) | |
193 | 198 … | //track whether we have successfully connected. |
194 | 199 … | //or how many failures there have been. |
195 | 200 … | var since = peer.stateChange |
196 | 201 … | peer.stateChange = Date.now() |
@@ -208,20 +213,4 @@ | ||
208 | 213 … | } |
209 | 214 … | } |
210 | 215 … | |
211 | 216 … | |
212 | - | |
213 | - | |
214 | - | |
215 | - | |
216 | - | |
217 | - | |
218 | - | |
219 | - | |
220 | - | |
221 | - | |
222 | - | |
223 | - | |
224 | - | |
225 | - | |
226 | - | |
227 | - |
plugins/gossip/init.js | ||
---|---|---|
@@ -1,6 +1,7 @@ | ||
1 | 1 … | var isArray = Array.isArray |
2 | 2 … | var pull = require('pull-stream') |
3 … | +var ref = require('ssb-ref') | |
3 | 4 … | |
4 | 5 … | module.exports = function (gossip, config, server) { |
5 | 6 … | |
6 | 7 … | // populate peertable with configured seeds (mainly used in testing) |
@@ -15,9 +16,10 @@ | ||
15 | 16 … | type: 'pub', live: true, keys: false |
16 | 17 … | }), |
17 | 18 … | pull.drain(function (msg) { |
18 | 19 … | if(!msg.content.address) return |
19 | - gossip.add(msg.content.address, 'pub') | |
20 … | + if(ref.isAddress(msg.content.address)) | |
21 … | + gossip.add(msg.content.address, 'pub') | |
20 | 22 … | }) |
21 | 23 … | ) |
22 | 24 … | |
23 | 25 … | // populate peertable with announcements on the LAN multicast |
plugins/gossip/schedule.js | ||
---|---|---|
@@ -124,16 +124,14 @@ | ||
124 | 124 … | |
125 | 125 … | if(connected.length > opts.quota) { |
126 | 126 … | return earliest(connected, connected.length - opts.quota) |
127 | 127 … | .forEach(function (peer) { |
128 | - console.log('Disconnect', name, u.stringifyAddress(peer)) | |
129 | 128 … | gossip.disconnect(peer) |
130 | 129 … | }) |
131 | 130 … | } |
132 | 131 … | |
133 | 132 … | select(peers, ts, and(filter, isOnline), opts) |
134 | 133 … | .forEach(function (peer) { |
135 | - console.log('Connect', name, u.stringifyAddress(peer)) | |
136 | 134 … | gossip.connect(peer) |
137 | 135 … | }) |
138 | 136 … | } |
139 | 137 … | |
@@ -203,7 +201,4 @@ | ||
203 | 201 … | exports.select = select |
204 | 202 … | |
205 | 203 … | |
206 | 204 … | |
207 | - | |
208 | - | |
209 | - |
plugins/invite.js | ||
---|---|---|
@@ -26,26 +26,8 @@ | ||
26 | 26 … | function isObject(o) { |
27 | 27 … | return o && 'object' === typeof o |
28 | 28 … | } |
29 | 29 … | |
30 | -function parseMultiServerInvite (invite) { | |
31 | - var parts = invite.split('~') | |
32 | - .map(function (e) { return e.split(':') }) | |
33 | - | |
34 | - if(parts.length !== 2) return null | |
35 | - if(!/^(net|wss?)$/.test(parts[0][0])) return null | |
36 | - if(parts[1][0] !== 'shs') return null | |
37 | - if(parts[1].length !== 3) return null | |
38 | - var p2 = invite.split(':') | |
39 | - p2.pop() | |
40 | - | |
41 | - return { | |
42 | - invite: invite, | |
43 | - remote: p2.join(':'), | |
44 | - key: '@'+p2.pop()+'.ed25519' | |
45 | - } | |
46 | -} | |
47 | - | |
48 | 30 … | module.exports = { |
49 | 31 … | name: 'invite', |
50 | 32 … | version: '1.0.0', |
51 | 33 … | manifest: mdm.manifest(apidoc), |
@@ -84,9 +66,9 @@ | ||
84 | 66 … | n = 1 |
85 | 67 … | modern = true |
86 | 68 … | } |
87 | 69 … | var addr = server.getAddress() |
88 | - var host = toAddress(addr).host | |
70 … | + var host = ref.parseAddress(addr).host | |
89 | 71 … | if(!config.allowPrivate && (ip.isPrivate(host) || 'localhost' === host)) |
90 | 72 … | return cb(new Error('Server has no public ip address, ' |
91 | 73 … | + 'cannot create useable invitation')) |
92 | 74 … | |
@@ -112,10 +94,12 @@ | ||
112 | 94 … | if(err) cb(err) |
113 | 95 … | else if(modern && server.ws && server.ws.getAddress) { |
114 | 96 … | cb(null, server.ws.getAddress()+':'+seed.toString('base64')) |
115 | 97 … | } |
116 | - else | |
117 | - cb(null, addr + '~' + seed.toString('base64')) | |
98 … | + else { | |
99 … | + addr = ref.parseAddress(addr) | |
100 … | + cb(null, [addr.host, addr.port, addr.key].join(':') + '~' + seed.toString('base64')) | |
101 … | + } | |
118 | 102 … | }) |
119 | 103 … | }, 'number|object'), |
120 | 104 … | use: valid.async(function (req, cb) { |
121 | 105 … | var rpc = this |
@@ -172,19 +156,21 @@ | ||
172 | 156 … | var opts |
173 | 157 … | // connect to the address in the invite code |
174 | 158 … | // using a keypair generated from the key-seed in the invite code |
175 | 159 … | var modern = false |
176 | - if(ref.isInvite(invite)) { //legacy invite | |
177 | - | |
178 | - var parts = invite.split('~') | |
179 | - opts = toAddress(parts[0])//.split(':') | |
180 | - //convert legacy code to multiserver invite code. | |
181 | - invite = 'net:'+opts.host+':'+opts.port+'~shs:'+opts.key.slice(1, -8)+':'+parts[1] | |
182 | - } else { | |
183 | - modern = true | |
184 | - opts = parseMultiServerInvite(invite) | |
160 … | + if(ref.isInvite(invite)) { //legacy ivite | |
161 … | + if(ref.isLegacyInvite(invite)) { | |
162 … | + var parts = invite.split('~') | |
163 … | + opts = ref.parseAddress(parts[0])//.split(':') | |
164 … | + //convert legacy code to multiserver invite code. | |
165 … | + invite = 'net:'+opts.host+':'+opts.port+'~shs:'+opts.key.slice(1, -8)+':'+parts[1] | |
166 … | + } | |
167 … | + else | |
168 … | + modern = true | |
185 | 169 … | } |
186 | 170 … | |
171 … | + opts = ref.parseAddress(ref.parseInvite(invite).remote) | |
172 … | + | |
187 | 173 … | ssbClient(null, { |
188 | 174 … | remote: invite, |
189 | 175 … | manifest: {invite: {use: 'async'}, getAddress: 'async'} |
190 | 176 … | }, function (err, rpc) { |
@@ -228,6 +214,4 @@ | ||
228 | 214 … | } |
229 | 215 … | } |
230 | 216 … | |
231 | 217 … | |
232 | - | |
233 | - |
plugins/replicate.js | ||
---|---|---|
@@ -5,9 +5,8 @@ | ||
5 | 5 … | var many = require('pull-many') |
6 | 6 … | var Cat = require('pull-cat') |
7 | 7 … | var Abort = require('pull-abortable') |
8 | 8 … | var Debounce = require('observ-debounce') |
9 | -var Observ = require('observ') | |
10 | 9 … | var mdm = require('mdmanifest') |
11 | 10 … | var apidoc = require('../lib/apidocs').replicate |
12 | 11 … | |
13 | 12 … | var Pushable = require('pull-pushable') |
@@ -217,6 +216,4 @@ | ||
217 | 216 … | } |
218 | 217 … | } |
219 | 218 … | |
220 | 219 … | |
221 | - | |
222 | - |
plugins/plugins.js | ||
---|---|---|
@@ -1,224 +1,0 @@ | ||
1 | -var assert = require('assert') | |
2 | -var path = require('path') | |
3 | -var fs = require('fs') | |
4 | -var pull = require('pull-stream') | |
5 | -var cat = require('pull-cat') | |
6 | -var many = require('pull-many') | |
7 | -var pushable = require('pull-pushable') | |
8 | -var toPull = require('stream-to-pull-stream') | |
9 | -var spawn = require('child_process').spawn | |
10 | -var mkdirp = require('mkdirp') | |
11 | -var osenv = require('osenv') | |
12 | -var rimraf = require('rimraf') | |
13 | -var mv = require('mv') | |
14 | -var mdm = require('mdmanifest') | |
15 | -var explain = require('explain-error') | |
16 | -var valid = require('../lib/validators') | |
17 | -var apidoc = require('../lib/apidocs').plugins | |
18 | - | |
19 | -module.exports = { | |
20 | - name: 'plugins', | |
21 | - version: '1.0.0', | |
22 | - manifest: mdm.manifest(apidoc), | |
23 | - permissions: { | |
24 | - master: {allow: ['install', 'uninstall', 'enable', 'disable']} | |
25 | - }, | |
26 | - init: function (server, config) { | |
27 | - var installPath = config.path | |
28 | - config.plugins = config.plugins || {} | |
29 | - mkdirp.sync(path.join(installPath, 'node_modules')) | |
30 | - | |
31 | - // helper to enable/disable plugins | |
32 | - function configPluginEnabled (b) { | |
33 | - return function (pluginName, cb) { | |
34 | - checkInstalled(pluginName, function (err) { | |
35 | - if (err) return cb(err) | |
36 | - | |
37 | - config.plugins[pluginName] = b | |
38 | - writePluginConfig(pluginName, b) | |
39 | - if (b) | |
40 | - cb(null, '\''+pluginName+'\' has been enabled. Restart Scuttlebot server to use the plugin.') | |
41 | - else | |
42 | - cb(null, '\''+pluginName+'\' has been disabled. Restart Scuttlebot server to stop using the plugin.') | |
43 | - }) | |
44 | - } | |
45 | - } | |
46 | - | |
47 | - // helper to check if a plugin is installed | |
48 | - function checkInstalled (pluginName, cb) { | |
49 | - if (!pluginName || typeof pluginName !== 'string') | |
50 | - return cb(new Error('plugin name is required')) | |
51 | - var modulePath = path.join(installPath, 'node_modules', pluginName) | |
52 | - fs.stat(modulePath, function (err) { | |
53 | - if (err) | |
54 | - cb(new Error('Plugin "'+pluginName+'" is not installed.')) | |
55 | - else | |
56 | - cb() | |
57 | - }) | |
58 | - } | |
59 | - | |
60 | - // write the plugin config to ~/.ssb/config | |
61 | - function writePluginConfig (pluginName, value) { | |
62 | - var cfgPath = path.join(config.path, 'config') | |
63 | - var existingConfig = {} | |
64 | - | |
65 | - // load ~/.ssb/config | |
66 | - try { existingConfig = JSON.parse(fs.readFileSync(cfgPath, 'utf-8')) } | |
67 | - catch (e) {} | |
68 | - | |
69 | - // update the plugins config | |
70 | - existingConfig.plugins = existingConfig.plugins || {} | |
71 | - existingConfig.plugins[pluginName] = value | |
72 | - | |
73 | - // write to disc | |
74 | - fs.writeFileSync(cfgPath, JSON.stringify(existingConfig, null, 2), 'utf-8') | |
75 | - } | |
76 | - | |
77 | - return { | |
78 | - install: valid.source(function (pluginName, opts) { | |
79 | - var p = pushable() | |
80 | - var dryRun = opts && opts['dry-run'] | |
81 | - var from = opts && opts.from | |
82 | - | |
83 | - if (!pluginName || typeof pluginName !== 'string') | |
84 | - return pull.error(new Error('plugin name is required')) | |
85 | - | |
86 | - // pull out the version, if given | |
87 | - if (pluginName.indexOf('@') !== -1) { | |
88 | - var pluginNameSplitted = pluginName.split('@') | |
89 | - pluginName = pluginNameSplitted[0] | |
90 | - var version = pluginNameSplitted[1] | |
91 | - | |
92 | - if (version && !from) | |
93 | - from = pluginName + '@' + version | |
94 | - } | |
95 | - | |
96 | - if (!validatePluginName(pluginName)) | |
97 | - return pull.error(new Error('invalid plugin name: "'+pluginName+'"')) | |
98 | - | |
99 | - // create a tmp directory to install into | |
100 | - var tmpInstallPath = path.join(osenv.tmpdir(), pluginName) | |
101 | - rimraf.sync(tmpInstallPath); mkdirp.sync(tmpInstallPath) | |
102 | - | |
103 | - // build args | |
104 | - // --global-style: dont dedup at the top level, gives proper isolation between each plugin | |
105 | - // --loglevel error: dont output warnings, because npm just whines about the lack of a package.json in ~/.ssb | |
106 | - var args = ['install', from||pluginName, '--global-style', '--loglevel', 'error'] | |
107 | - if (dryRun) | |
108 | - args.push('--dry-run') | |
109 | - | |
110 | - // exec npm | |
111 | - var child = spawn('npm', args, { cwd: tmpInstallPath }) | |
112 | - .on('close', function (code) { | |
113 | - if (code == 0 && !dryRun) { | |
114 | - var tmpInstallNMPath = path.join(tmpInstallPath, 'node_modules') | |
115 | - var finalInstallNMPath = path.join(installPath, 'node_modules') | |
116 | - | |
117 | - // delete plugin, if it's already there | |
118 | - rimraf.sync(path.join(finalInstallNMPath, pluginName)) | |
119 | - | |
120 | - // move the plugin from the tmpdir into our install path | |
121 | - // ...using our given plugin name | |
122 | - var dirs = fs.readdirSync(tmpInstallNMPath) | |
123 | - .filter(function (name) { return name.charAt(0) !== '.' }) // filter out dot dirs, like '.bin' | |
124 | - mv( | |
125 | - path.join(tmpInstallNMPath, dirs[0]), | |
126 | - path.join(finalInstallNMPath, pluginName), | |
127 | - function (err) { | |
128 | - if (err) | |
129 | - return p.end(explain(err, '"'+pluginName+'" failed to install. See log output above.')) | |
130 | - | |
131 | - // enable the plugin | |
132 | - // - use basename(), because plugins can be installed from the FS, in which case pluginName is a path | |
133 | - var name = path.basename(pluginName) | |
134 | - config.plugins[name] = true | |
135 | - writePluginConfig(name, true) | |
136 | - p.push(new Buffer('"'+pluginName+'" has been installed. Restart Scuttlebot server to enable the plugin.\n', 'utf-8')) | |
137 | - p.end() | |
138 | - } | |
139 | - ) | |
140 | - } else | |
141 | - p.end(new Error('"'+pluginName+'" failed to install. See log output above.')) | |
142 | - }) | |
143 | - return cat([ | |
144 | - pull.values([new Buffer('Installing "'+pluginName+'"...\n', 'utf-8')]), | |
145 | - many([toPull(child.stdout), toPull(child.stderr)]), | |
146 | - p | |
147 | - ]) | |
148 | - }, 'string', 'object?'), | |
149 | - uninstall: valid.source(function (pluginName, opts) { | |
150 | - var p = pushable() | |
151 | - if (!pluginName || typeof pluginName !== 'string') | |
152 | - return pull.error(new Error('plugin name is required')) | |
153 | - | |
154 | - var modulePath = path.join(installPath, 'node_modules', pluginName) | |
155 | - | |
156 | - rimraf(modulePath, function (err) { | |
157 | - if (!err) { | |
158 | - writePluginConfig(pluginName, false) | |
159 | - p.push(new Buffer('"'+pluginName+'" has been uninstalled. Restart Scuttlebot server to disable the plugin.\n', 'utf-8')) | |
160 | - p.end() | |
161 | - } else | |
162 | - p.end(err) | |
163 | - }) | |
164 | - return p | |
165 | - }, 'string', 'object?'), | |
166 | - enable: valid.async(configPluginEnabled(true), 'string'), | |
167 | - disable: valid.async(configPluginEnabled(false), 'string') | |
168 | - } | |
169 | - } | |
170 | -} | |
171 | - | |
172 | -module.exports.loadUserPlugins = function (createSbot, config) { | |
173 | - // iterate all modules | |
174 | - var nodeModulesPath = path.join(config.path, 'node_modules') | |
175 | - try { | |
176 | - console.log('Loading plugins from', nodeModulesPath) | |
177 | - fs.readdirSync(nodeModulesPath).forEach(function (filename) { | |
178 | - if (filename.charAt(0) == '.') | |
179 | - return // ignore | |
180 | - if (!config.plugins[filename]) | |
181 | - return console.log('Skipping disabled plugin "'+filename+'"') | |
182 | - console.log('Loading plugin "'+filename+'"') | |
183 | - | |
184 | - try { | |
185 | - // load module | |
186 | - var plugin = require(path.join(nodeModulesPath, filename)) | |
187 | - | |
188 | - // check the signature | |
189 | - assertSbotPlugin(plugin) | |
190 | - | |
191 | - // load | |
192 | - createSbot.use(plugin) | |
193 | - } catch (e) { | |
194 | - console.error('Error loading plugin "'+filename+'":', e.message) | |
195 | - } | |
196 | - }) | |
197 | - } catch (e) { | |
198 | - // node_modules dne, ignore | |
199 | - } | |
200 | -} | |
201 | - | |
202 | -// predictate to check if an object appears to be a sbot plugin | |
203 | -function assertSbotPlugin (obj) { | |
204 | - // function signature: | |
205 | - if (typeof obj == 'function') | |
206 | - return | |
207 | - | |
208 | - // object signature: | |
209 | - assert(obj && typeof obj == 'object', 'module.exports must be an object') | |
210 | - assert(typeof obj.name == 'string', 'module.exports.name must be a string') | |
211 | - assert(typeof obj.version == 'string', 'module.exports.version must be a string') | |
212 | - assert(obj.manifest && | |
213 | - typeof obj.manifest == 'object', 'module.exports.manifest must be an object') | |
214 | - assert(typeof obj.init == 'function', 'module.exports.init must be a function') | |
215 | -} | |
216 | - | |
217 | -function validatePluginName (name) { | |
218 | - if (/^[._]/.test(name)) | |
219 | - return false | |
220 | - // from npm-validate-package-name: | |
221 | - if (encodeURIComponent(name) !== name) | |
222 | - return false | |
223 | - return true | |
224 | -} |
test/invite.js | ||
---|---|---|
@@ -4,8 +4,9 @@ | ||
4 | 4 … | var explain = require('explain-error') |
5 | 5 … | var pull = require('pull-stream') |
6 | 6 … | var u = require('../lib/util') |
7 | 7 … | var ssbClient = require('ssb-client') |
8 … | +var ref = require('ssb-ref') | |
8 | 9 … | |
9 | 10 … | var createSbot = require('../') |
10 | 11 … | .use(require('../plugins/master')) |
11 | 12 … | .use(require('../plugins/invite')) |
@@ -15,66 +16,8 @@ | ||
15 | 16 … | function all(stream, cb) { |
16 | 17 … | return pull(stream, pull.collect(cb)) |
17 | 18 … | } |
18 | 19 … | |
19 | -tape('test invite api', function (t) { | |
20 | - | |
21 | - var aliceKeys = ssbKeys.generate() | |
22 | - | |
23 | - var alice = createSbot({ | |
24 | - temp: 'test-invite-alice', timeout: 1000, | |
25 | - allowPrivate: true, | |
26 | - keys: aliceKeys | |
27 | - }) | |
28 | - | |
29 | - var bobKeys = ssbKeys.generate() | |
30 | - var bob = alice.createFeed(bobKeys) //bob | |
31 | - | |
32 | - //request a secret that with particular permissions. | |
33 | - | |
34 | - createSbot.createClient({keys: aliceKeys}) | |
35 | - (alice.getAddress(), function (err, rpc) { | |
36 | - if(err) throw err | |
37 | - | |
38 | - rpc.invite.create(1, function (err, invite) { | |
39 | - if(err) throw explain(err, 'cannot create invite code') | |
40 | - | |
41 | - var parts = invite.split('~') | |
42 | - console.log(parts) | |
43 | - createSbot.createClient({seed: parts[1]}) | |
44 | - (parts[0], function (err, rpc2) { | |
45 | - if(err) throw err | |
46 | - | |
47 | - rpc2.invite.use({ | |
48 | - feed: bob.id | |
49 | - }, function (err, msg) { | |
50 | - if(err) throw explain(err, 'bob cannot use invite code') | |
51 | - | |
52 | - pull( | |
53 | - rpc.links({dest: bob.id, rel: 'contact', source: '@', keys: false}), | |
54 | - pull.collect(function (err, ary) { | |
55 | - if(err) throw err | |
56 | - | |
57 | - var followed = ary[0] | |
58 | - delete followed.message | |
59 | - | |
60 | - t.deepEqual( | |
61 | - ary[0], | |
62 | - {source: alice.id, dest: bob.id, rel: 'contact'} | |
63 | - ) | |
64 | - | |
65 | - alice.close(true) | |
66 | - console.log('done') | |
67 | - t.end() | |
68 | - | |
69 | - }) | |
70 | - ) | |
71 | - }) | |
72 | - }) | |
73 | - }) | |
74 | - }) | |
75 | -}) | |
76 | - | |
77 | 20 … | tape('test invite.accept api', function (t) { |
78 | 21 … | |
79 | 22 … | var alice = createSbot({ |
80 | 23 … | temp: 'test-invite-alice2', timeout: 100, |
@@ -148,11 +91,12 @@ | ||
148 | 91 … | all(bob.messagesByType('pub'), function (err, ary) { |
149 | 92 … | if(err) throw err |
150 | 93 … | t.equal(ary.length, 1) |
151 | 94 … | |
95 … | + console.log(ary) | |
152 | 96 … | t.deepEqual({ |
153 | 97 … | type: 'pub', |
154 | - address: u.toAddress(alice.address()), | |
98 … | + address: ref.parseAddress(alice.address()), | |
155 | 99 … | }, ary[0].value.content) |
156 | 100 … | |
157 | 101 … | all(bob.messagesByType('contact'), function (err, ary) { |
158 | 102 … | if(err) throw err |
@@ -201,9 +145,13 @@ | ||
201 | 145 … | alice.invite.create(1, function (err, invite) { |
202 | 146 … | if(err) throw err |
203 | 147 … | |
204 | 148 … | // use a local ipv6 address in the invite |
205 | - var inviteV6 = invite.replace(/^[0-9.]*/, '::1') | |
149 … | +// if(/localhost/.test(invite)) | |
150 … | + | |
151 … | + var inviteV6 | |
152 … | + | |
153 … | + = invite.replace(/localhost|([0-9.]*)/, '::1') | |
206 | 154 … | console.log(inviteV6, invite) |
207 | 155 … | |
208 | 156 … | bob.invite.accept(inviteV6, function (err, msg) { |
209 | 157 … | if(err) throw err |
@@ -304,4 +252,8 @@ | ||
304 | 252 … | |
305 | 253 … | |
306 | 254 … | }) |
307 | 255 … | |
256 … | + | |
257 … | + | |
258 … | + | |
259 … | + |
test/master.js | ||
---|---|---|
@@ -17,22 +17,8 @@ | ||
17 | 17 … | keys: aliceKeys |
18 | 18 … | }) |
19 | 19 … | |
20 | 20 … | tape('connect remote master', function (t) { |
21 | - | |
22 | - t.equal(alice.getAddress(), 'localhost:45451:'+alice.id) | |
23 | - | |
24 | - t.deepEqual( | |
25 | - util.parseAddress(alice.getAddress()), { | |
26 | - host: 'localhost', | |
27 | - port: 45451, | |
28 | - key: alice.id | |
29 | - }) | |
30 | - | |
31 | - t.end() | |
32 | -}) | |
33 | - | |
34 | -tape('connect remote master', function (t) { | |
35 | 21 … | createSbot.createClient({keys: bobKeys}) |
36 | 22 … | (alice.getAddress(), function (err, rpc) { |
37 | 23 … | if(err) throw err |
38 | 24 … | rpc.publish({ |
@@ -57,4 +43,5 @@ | ||
57 | 43 … | }) |
58 | 44 … | }) |
59 | 45 … | }) |
60 | 46 … | |
47 … | + |
test/server.js | ||
---|---|---|
@@ -6,10 +6,8 @@ | ||
6 | 6 … | var ssbKeys = require('ssb-keys') |
7 | 7 … | |
8 | 8 … | var u = require('./util') |
9 | 9 … | |
10 | -var toAddr = require('../lib/util').toAddress | |
11 | - | |
12 | 10 … | // create 3 servers |
13 | 11 … | // give them all pub servers (on localhost) |
14 | 12 … | // and get them to follow each other... |
15 | 13 … | |
@@ -93,4 +91,5 @@ | ||
93 | 91 … | }) |
94 | 92 … | |
95 | 93 … | |
96 | 94 … | |
95 … | + |
test/util.js | ||
---|---|---|
@@ -1,5 +1,5 @@ | ||
1 | -var toAddress = require('../lib/util').parseAddress | |
1 … | +var ref = require('ssb-ref') | |
2 | 2 … | |
3 | 3 … | exports.follow = function (id) { |
4 | 4 … | return { |
5 | 5 … | type: 'contact', contact: id, following: true |
@@ -18,9 +18,9 @@ | ||
18 | 18 … | |
19 | 19 … | exports.pub = function (address) { |
20 | 20 … | return { |
21 | 21 … | type: 'pub', |
22 | - address: toAddress(address) | |
22 … | + address: ref.parseAddress(address) | |
23 | 23 … | } |
24 | 24 … | } |
25 | 25 … | |
26 | 26 … | exports.file = function (hash) { |
@@ -28,4 +28,5 @@ | ||
28 | 28 … | type: 'file', |
29 | 29 … | file: hash |
30 | 30 … | } |
31 | 31 … | } |
32 … | + |
test/plugins.js | ||
---|---|---|
@@ -1,195 +1,0 @@ | ||
1 | -var ssbKeys = require('ssb-keys') | |
2 | -var tape = require('tape') | |
3 | -var u = require('./util') | |
4 | -var pull = require('pull-stream') | |
5 | -var osenv = require('osenv') | |
6 | -var path = require('path') | |
7 | -var fs = require('fs') | |
8 | -var createSbot = require('../') | |
9 | - | |
10 | -var initialPlugins = createSbot.plugins.slice() | |
11 | -function resetSbot () { | |
12 | - createSbot.plugins = initialPlugins.slice() | |
13 | - createSbot.use(require('../plugins/plugins')) | |
14 | -} | |
15 | - | |
16 | -tape('install and load plugins', function (t) { | |
17 | - | |
18 | - var aliceKeys = ssbKeys.generate() | |
19 | - var datadirPath = path.join(osenv.tmpdir(), 'test-plugins1') | |
20 | - | |
21 | - t.test('install plugin', function (t) { | |
22 | - | |
23 | - resetSbot() | |
24 | - var sbot = createSbot({ | |
25 | - path: datadirPath, | |
26 | - port: 45451, host: 'localhost', | |
27 | - keys: aliceKeys | |
28 | - }) | |
29 | - | |
30 | - console.log('installing plugin...') | |
31 | - pull( | |
32 | - sbot.plugins.install('test-plugin', { from: __dirname + '/test-plugin' }), | |
33 | - pull.collect(function (err, out) { | |
34 | - if (err) throw err | |
35 | - console.log(out.map(function (b) { return b.toString('utf-8') }).join('')) | |
36 | - | |
37 | - t.ok(fs.statSync(path.join(datadirPath, 'node_modules/test-plugin'))) | |
38 | - | |
39 | - sbot.close(function () { | |
40 | - t.end() | |
41 | - }) | |
42 | - }) | |
43 | - ) | |
44 | - }) | |
45 | - | |
46 | - t.test('installed and enabled plugin is loaded', function (t) { | |
47 | - | |
48 | - var config = { | |
49 | - path: datadirPath, | |
50 | - port: 45451, host: 'localhost', | |
51 | - keys: aliceKeys, | |
52 | - plugins: { | |
53 | - 'test-plugin': true | |
54 | - } | |
55 | - } | |
56 | - resetSbot() | |
57 | - require('../plugins/plugins').loadUserPlugins(createSbot, config) | |
58 | - var sbot = createSbot(config) | |
59 | - | |
60 | - t.ok(sbot.test) | |
61 | - t.ok(sbot.test.ping) | |
62 | - | |
63 | - sbot.test.ping('ping', function (err, res) { | |
64 | - if (err) throw err | |
65 | - t.equal(res, 'ping pong') | |
66 | - | |
67 | - sbot.close(function () { | |
68 | - t.end() | |
69 | - }) | |
70 | - }) | |
71 | - }) | |
72 | - | |
73 | - t.test('installed and disabled plugin is not loaded', function (t) { | |
74 | - | |
75 | - var config = { | |
76 | - path: datadirPath, | |
77 | - port: 45451, host: 'localhost', | |
78 | - keys: aliceKeys, | |
79 | - plugins: { | |
80 | - 'test-plugin': false | |
81 | - } | |
82 | - } | |
83 | - resetSbot() | |
84 | - require('../plugins/plugins').loadUserPlugins(createSbot, config) | |
85 | - var sbot = createSbot(config) | |
86 | - | |
87 | - t.equal(sbot.test, undefined) | |
88 | - | |
89 | - sbot.close(function () { | |
90 | - t.end() | |
91 | - }) | |
92 | - }) | |
93 | - | |
94 | - t.test('uninstall plugin', function (t) { | |
95 | - | |
96 | - resetSbot() | |
97 | - var sbot = createSbot({ | |
98 | - path: datadirPath, | |
99 | - port: 45451, host: 'localhost', | |
100 | - keys: aliceKeys | |
101 | - }) | |
102 | - | |
103 | - console.log('uninstalling plugin...') | |
104 | - pull( | |
105 | - sbot.plugins.uninstall('test-plugin'), | |
106 | - pull.collect(function (err, out) { | |
107 | - if (err) throw err | |
108 | - console.log(out.map(function (b) { return b.toString('utf-8') }).join('')) | |
109 | - | |
110 | - t.throws(function () { fs.statSync(path.join(datadirPath, 'node_modules/test-plugin')) }) | |
111 | - | |
112 | - sbot.close(function () { | |
113 | - t.end() | |
114 | - }) | |
115 | - }) | |
116 | - ) | |
117 | - }) | |
118 | - | |
119 | - t.test('install plugin under custom name', function (t) { | |
120 | - | |
121 | - resetSbot() | |
122 | - var sbot = createSbot({ | |
123 | - path: datadirPath, | |
124 | - port: 45451, host: 'localhost', | |
125 | - keys: aliceKeys | |
126 | - }) | |
127 | - | |
128 | - console.log('installing plugin...') | |
129 | - pull( | |
130 | - sbot.plugins.install('my-test-plugin', { from: __dirname + '/test-plugin' }), | |
131 | - pull.collect(function (err, out) { | |
132 | - if (err) throw err | |
133 | - console.log(out.map(function (b) { return b.toString('utf-8') }).join('')) | |
134 | - | |
135 | - t.ok(fs.statSync(path.join(datadirPath, 'node_modules/my-test-plugin'))) | |
136 | - | |
137 | - sbot.close(function () { | |
138 | - t.end() | |
139 | - }) | |
140 | - }) | |
141 | - ) | |
142 | - }) | |
143 | - | |
144 | - t.test('installed and enabled plugin is loaded, under custom name', function (t) { | |
145 | - | |
146 | - var config = { | |
147 | - path: datadirPath, | |
148 | - port: 45451, host: 'localhost', | |
149 | - keys: aliceKeys, | |
150 | - plugins: { | |
151 | - 'my-test-plugin': true | |
152 | - } | |
153 | - } | |
154 | - resetSbot() | |
155 | - require('../plugins/plugins').loadUserPlugins(createSbot, config) | |
156 | - var sbot = createSbot(config) | |
157 | - | |
158 | - t.ok(sbot.test) | |
159 | - t.ok(sbot.test.ping) | |
160 | - | |
161 | - sbot.test.ping('ping', function (err, res) { | |
162 | - if (err) throw err | |
163 | - t.equal(res, 'ping pong') | |
164 | - | |
165 | - sbot.close(function () { | |
166 | - t.end() | |
167 | - }) | |
168 | - }) | |
169 | - }) | |
170 | - | |
171 | - t.test('uninstall plugin under custom name', function (t) { | |
172 | - | |
173 | - resetSbot() | |
174 | - var sbot = createSbot({ | |
175 | - path: datadirPath, | |
176 | - port: 45451, host: 'localhost', | |
177 | - keys: aliceKeys | |
178 | - }) | |
179 | - | |
180 | - console.log('uninstalling plugin...') | |
181 | - pull( | |
182 | - sbot.plugins.uninstall('my-test-plugin'), | |
183 | - pull.collect(function (err, out) { | |
184 | - if (err) throw err | |
185 | - console.log(out.map(function (b) { return b.toString('utf-8') }).join('')) | |
186 | - | |
187 | - t.throws(function () { fs.statSync(path.join(datadirPath, 'node_modules/my-test-plugin')) }) | |
188 | - | |
189 | - sbot.close(function () { | |
190 | - t.end() | |
191 | - }) | |
192 | - }) | |
193 | - ) | |
194 | - }) | |
195 | -}) |
Built with git-ssb-web