Commit b7fa14294c552a418b5403b44385f0761fce9638
minimum viable decent
Ev Bogue committed on 4/20/2018, 4:56:34 AMFiles changed
.gitignore | added |
bin.js | added |
config.js | added |
index.js | added |
keys.js | added |
manifest.json | added |
package-lock.json | added |
package.json | added |
render.js | added |
scuttlebot.js | added |
style.css | added |
style.css.json | added |
style.js | added |
views.js | added |
bin.js | ||
---|---|---|
@@ -1,0 +1,136 @@ | ||
1 … | +var fs = require('fs') | |
2 … | +var path = require('path') | |
3 … | +var ssbKeys = require('ssb-keys') | |
4 … | +var stringify = require('pull-stringify') | |
5 … | +var open = require('opn') | |
6 … | +var home = require('os-homedir')() | |
7 … | +var nonPrivate = require('non-private-ip') | |
8 … | +var muxrpcli = require('muxrpcli') | |
9 … | + | |
10 … | +var SEC = 1e3 | |
11 … | +var MIN = 60*SEC | |
12 … | + | |
13 … | +var config = { | |
14 … | + name: 'ssb', | |
15 … | + host: nonPrivate.v4 || '', | |
16 … | + timeout: 0, | |
17 … | + local: 'true', | |
18 … | + port: 8008, | |
19 … | + path: path.join(home, '.ssb'), | |
20 … | + ws: { port: 8989 }, | |
21 … | + caps: { | |
22 … | + shs: '1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=', | |
23 … | + sign: null | |
24 … | + }, | |
25 … | + friends: { | |
26 … | + dunbar: 150, | |
27 … | + hops: 3 | |
28 … | + }, | |
29 … | + gossip: { | |
30 … | + connections: 3 | |
31 … | + }, | |
32 … | + timers: { | |
33 … | + connection: 0, | |
34 … | + reconnect: 5*SEC, | |
35 … | + ping: 5*MIN, | |
36 … | + handshake: 5*SEC | |
37 … | + }, | |
38 … | + master: [], | |
39 … | + party: true | |
40 … | +} | |
41 … | + | |
42 … | +config.keys = ssbKeys.loadOrCreateSync(path.join(config.path, 'secret')) | |
43 … | + | |
44 … | +var coraClient = fs.readFileSync(path.join('./build/index.html')) | |
45 … | + | |
46 … | +var manifestFile = path.join(config.path, 'manifest.json') | |
47 … | + | |
48 … | +var argv = process.argv.slice(2) | |
49 … | +var i = argv.indexOf('--') | |
50 … | +var conf = argv.slice(i+1) | |
51 … | +argv = ~i ? argv.slice(0, i) : argv | |
52 … | + | |
53 … | +if (argv[0] == 'server') { | |
54 … | + | |
55 … | + var createSbot = require('scuttlebot') | |
56 … | + .use(require('scuttlebot/plugins/master')) | |
57 … | + .use(require('scuttlebot/plugins/gossip')) | |
58 … | + .use(require('scuttlebot/plugins/replicate')) | |
59 … | + .use(require('ssb-friends')) | |
60 … | + .use(require('ssb-blobs')) | |
61 … | + .use(require('ssb-query')) | |
62 … | + .use(require('ssb-links')) | |
63 … | + .use(require('ssb-ebt')) | |
64 … | + .use(require('scuttlebot/plugins/invite')) | |
65 … | + .use(require('scuttlebot/plugins/local')) | |
66 … | + .use(require('decent-ssb/plugins/ws')) | |
67 … | + .use({ | |
68 … | + name: 'serve', | |
69 … | + version: '1.0.0', | |
70 … | + init: function (sbot) { | |
71 … | + sbot.ws.use(function (req, res, next) { | |
72 … | + var send = {} | |
73 … | + send = config | |
74 … | + delete send.keys // very important to keep this, as it removes the server keys from the config before broadcast | |
75 … | + send.address = sbot.ws.getAddress() | |
76 … | + if(req.url == '/') | |
77 … | + res.end(coraClient) | |
78 … | + if(req.url == '/get-config') | |
79 … | + res.end(JSON.stringify(send)) | |
80 … | + else next() | |
81 … | + }) | |
82 … | + } | |
83 … | + }) | |
84 … | + | |
85 … | + open('http://localhost:' + config.ws.port, {wait: false}) | |
86 … | + | |
87 … | + var server = createSbot(config) | |
88 … | + | |
89 … | + fs.writeFileSync(manifestFile, JSON.stringify(server.getManifest(), null, 2)) | |
90 … | +} else { | |
91 … | + | |
92 … | + var manifest | |
93 … | + try { | |
94 … | + manifest = JSON.parse(fs.readFileSync(manifestFile)) | |
95 … | + } catch (err) { | |
96 … | + throw explain(err, | |
97 … | + 'no manifest file' | |
98 … | + + '- should be generated first time server is run' | |
99 … | + ) | |
100 … | + } | |
101 … | + | |
102 … | + // connect | |
103 … | + require('ssb-client')(config.keys, { | |
104 … | + manifest: manifest, | |
105 … | + port: config.port, | |
106 … | + host: config.host||'localhost', | |
107 … | + caps: config.caps, | |
108 … | + key: config.key || config.keys.id | |
109 … | + }, function (err, rpc) { | |
110 … | + if(err) { | |
111 … | + if (/could not connect/.test(err.message)) { | |
112 … | + console.log('Error: Could not connect to the scuttlebot server.') | |
113 … | + console.log('Use the "server" command to start it.') | |
114 … | + if(config.verbose) throw err | |
115 … | + process.exit(1) | |
116 … | + } | |
117 … | + throw err | |
118 … | + } | |
119 … | + | |
120 … | + // add some extra commands | |
121 … | + manifest.version = 'async' | |
122 … | + manifest.config = 'sync' | |
123 … | + rpc.version = function (cb) { | |
124 … | + console.log(require('./package.json').version) | |
125 … | + cb() | |
126 … | + } | |
127 … | + rpc.config = function (cb) { | |
128 … | + console.log(JSON.stringify(config, null, 2)) | |
129 … | + cb() | |
130 … | + } | |
131 … | + | |
132 … | + // run commandline flow | |
133 … | + muxrpcli(argv, manifest, rpc, config.verbose) | |
134 … | + }) | |
135 … | +} | |
136 … | + |
config.js | ||
---|---|---|
@@ -1,0 +1,24 @@ | ||
1 … | +var http = require('http') | |
2 … | + | |
3 … | +module.exports = function () { | |
4 … | + var host = window.location.origin | |
5 … | + | |
6 … | + http.get(host + '/get-config', function (res) { | |
7 … | + res.on('data', function (data, remote) { | |
8 … | + config = data | |
9 … | + localStorage[host] = config | |
10 … | + }) | |
11 … | + }) | |
12 … | + | |
13 … | + var config = JSON.parse(localStorage[host]) | |
14 … | + | |
15 … | + config.blobsUrl = host + '/blobs/get/' | |
16 … | + config.emojiUrl = host + '/img/emoji/' | |
17 … | + | |
18 … | + if (config.ws.remote) | |
19 … | + config.remote = config.ws.remote | |
20 … | + else | |
21 … | + config.remote = config.address | |
22 … | + | |
23 … | + return config | |
24 … | +} |
index.js | ||
---|---|---|
@@ -1,0 +1,17 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | + | |
3 … | +var views = require('./views') | |
4 … | + | |
5 … | +document.head.appendChild(h('style', require('./style.css.json'))) | |
6 … | + | |
7 … | +var src = window.location.hash | |
8 … | + | |
9 … | +window.onhashchange = function () { | |
10 … | + window.location.reload() | |
11 … | +} | |
12 … | + | |
13 … | +if (src == '#raw') { | |
14 … | + views.rawstream() | |
15 … | +} else { | |
16 … | + views.logstream() | |
17 … | +} |
keys.js | ||
---|---|---|
@@ -1,0 +1,6 @@ | ||
1 … | + | |
2 … | +var config = require('./config')() | |
3 … | +var ssbKeys = require('ssb-keys') | |
4 … | +var path = require('path') | |
5 … | + | |
6 … | +module.exports = ssbKeys.loadOrCreateSync(path.join(config.caps.shs + '/secret')) |
manifest.json | ||
---|---|---|
@@ -1,0 +1,74 @@ | ||
1 … | +{ | |
2 … | + "auth": "async", | |
3 … | + "address": "sync", | |
4 … | + "manifest": "sync", | |
5 … | + "get": "async", | |
6 … | + "createFeedStream": "source", | |
7 … | + "createLogStream": "source", | |
8 … | + "messagesByType": "source", | |
9 … | + "createHistoryStream": "source", | |
10 … | + "createUserStream": "source", | |
11 … | + "links": "source", | |
12 … | + "relatedMessages": "async", | |
13 … | + "add": "async", | |
14 … | + "publish": "async", | |
15 … | + "getAddress": "sync", | |
16 … | + "getLatest": "async", | |
17 … | + "latest": "source", | |
18 … | + "latestSequence": "async", | |
19 … | + "whoami": "sync", | |
20 … | + "usage": "sync", | |
21 … | + "gossip": { | |
22 … | + "peers": "sync", | |
23 … | + "add": "sync", | |
24 … | + "ping": "duplex", | |
25 … | + "connect": "async", | |
26 … | + "changes": "source", | |
27 … | + "reconnect": "sync" | |
28 … | + }, | |
29 … | + "friends": { | |
30 … | + "all": "async", | |
31 … | + "hops": "async", | |
32 … | + "createFriendStream": "source", | |
33 … | + "get": "sync" | |
34 … | + }, | |
35 … | + "replicate": { | |
36 … | + "changes": "source" | |
37 … | + }, | |
38 … | + "invite": { | |
39 … | + "create": "async", | |
40 … | + "accept": "async", | |
41 … | + "use": "async" | |
42 … | + }, | |
43 … | + "block": { | |
44 … | + "isBlocked": "sync" | |
45 … | + }, | |
46 … | + "private": { | |
47 … | + "publish": "async", | |
48 … | + "unbox": "sync" | |
49 … | + }, | |
50 … | + "blobs": { | |
51 … | + "get": "source", | |
52 … | + "add": "sink", | |
53 … | + "ls": "source", | |
54 … | + "has": "async", | |
55 … | + "size": "async", | |
56 … | + "meta": "async", | |
57 … | + "want": "async", | |
58 … | + "push": "async", | |
59 … | + "changes": "source", | |
60 … | + "createWants": "source" | |
61 … | + }, | |
62 … | + "links2": { | |
63 … | + "read": "source", | |
64 … | + "dump": "source" | |
65 … | + }, | |
66 … | + "query": { | |
67 … | + "read": "source", | |
68 … | + "dump": "source" | |
69 … | + }, | |
70 … | + "ws": {}, | |
71 … | + "fulltext": { | |
72 … | + "search": "source" | |
73 … | + } | |
74 … | +} |
package-lock.json | ||
---|---|---|
The diff is too large to show. Use a local git client to view these changes. Old file size: 0 bytes New file size: 215241 bytes |
package.json | ||
---|---|---|
@@ -1,0 +1,42 @@ | ||
1 … | +{ | |
2 … | + "name": "mvd", | |
3 … | + "version": "1.0.0", | |
4 … | + "description": "minimum viable decent", | |
5 … | + "main": "index.js", | |
6 … | + "scripts": { | |
7 … | + "start": "node bin server", | |
8 … | + "build": "node style.js && mkdir -p build && browserify index.js | indexhtmlify > build/index.html" | |
9 … | + }, | |
10 … | + "devDependencies": { | |
11 … | + "browserify": "^14.5.0", | |
12 … | + "indexhtmlify": "^1.3.1" | |
13 … | + }, | |
14 … | + "author": "Ev Bogue <ev@evbogue.com>", | |
15 … | + "license": "MIT", | |
16 … | + "dependencies": { | |
17 … | + "decent-ssb": "^4.3.0", | |
18 … | + "human-time": "0.0.1", | |
19 … | + "hyperloadmore": "^1.1.0", | |
20 … | + "hyperscript": "^2.0.2", | |
21 … | + "hyperscroll": "^1.0.0", | |
22 … | + "muxrpcli": "^1.1.0", | |
23 … | + "non-private-ip": "^1.4.3", | |
24 … | + "opn": "^5.3.0", | |
25 … | + "os-homedir": "^1.0.2", | |
26 … | + "patchapp-raw": "^1.0.1", | |
27 … | + "pull-more": "^1.1.0", | |
28 … | + "pull-reconnect": "0.0.3", | |
29 … | + "pull-stream": "^3.6.7", | |
30 … | + "pull-stringify": "^2.0.0", | |
31 … | + "scuttlebot": "^11.2.1", | |
32 … | + "ssb-blobs": "^1.1.4", | |
33 … | + "ssb-client": "^4.5.7", | |
34 … | + "ssb-ebt": "^5.1.5", | |
35 … | + "ssb-friends": "^2.4.0", | |
36 … | + "ssb-keys": "^7.0.14", | |
37 … | + "ssb-links": "^3.0.3", | |
38 … | + "ssb-markdown": "^3.6.0", | |
39 … | + "ssb-query": "^2.0.1", | |
40 … | + "ssb-ref": "^2.9.1" | |
41 … | + } | |
42 … | +} |
render.js | ||
---|---|---|
@@ -1,0 +1,15 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | + | |
3 … | +var human = require('human-time') | |
4 … | + | |
5 … | +var markdown = require('ssb-markdown') | |
6 … | + | |
7 … | +module.exports = function (msg) { | |
8 … | + if (msg.value.content.type == 'post') { | |
9 … | + return h('div.message__content', | |
10 … | + h('span.timestamp', h('a', {href: msg.key}, human(new Date(msg.value.timestamp)))), | |
11 … | + msg.value.author, | |
12 … | + msg.value.content.text | |
13 … | + ) | |
14 … | + } else { return } | |
15 … | +} |
scuttlebot.js | ||
---|---|---|
@@ -1,0 +1,45 @@ | ||
1 … | +var pull = require('pull-stream') | |
2 … | +var ssbKeys = require('ssb-keys') | |
3 … | +var ref = require('ssb-ref') | |
4 … | +var reconnect = require('pull-reconnect') | |
5 … | + | |
6 … | +var config = require('./config')() | |
7 … | +var createClient = require('ssb-client') | |
8 … | +var keys = require('./keys') | |
9 … | + | |
10 … | +var CACHE = {} | |
11 … | + | |
12 … | +var rec = reconnect(function (isConn) { | |
13 … | + function notify (value) { | |
14 … | + isConn(value) | |
15 … | + } | |
16 … | + | |
17 … | + createClient(keys, { | |
18 … | + manifest: require('./manifest.json'), | |
19 … | + remote: config.remote, | |
20 … | + caps: config.caps | |
21 … | + }, function (err, _sbot) { | |
22 … | + if(err) | |
23 … | + return notify(err) | |
24 … | + | |
25 … | + sbot = _sbot | |
26 … | + sbot.on('closed', function () { | |
27 … | + sbot = null | |
28 … | + notify(new Error('closed')) | |
29 … | + }) | |
30 … | + | |
31 … | + notify() | |
32 … | + }) | |
33 … | +}) | |
34 … | + | |
35 … | +module.exports = { | |
36 … | + createLogStream: rec.source(function (opts) { | |
37 … | + return pull( | |
38 … | + sbot.createLogStream(opts), | |
39 … | + pull.through(function (e) { | |
40 … | + CACHE[e.key] = CACHE[e.key] || e.value | |
41 … | + }) | |
42 … | + ) | |
43 … | + }) | |
44 … | +} | |
45 … | + |
style.css | ||
---|---|---|
@@ -1,0 +1,16 @@ | ||
1 … | +.hyperscroll { | |
2 … | + width: 100%; | |
3 … | +} | |
4 … | + | |
5 … | +.hyperscroll > .content { | |
6 … | + max-width: 680px; | |
7 … | + margin-left: auto; | |
8 … | + margin-right: auto; | |
9 … | +} | |
10 … | + | |
11 … | + | |
12 … | +.message { | |
13 … | + display: block; | |
14 … | + margin-top: .25em; | |
15 … | + margin-bottom: .25em; | |
16 … | +} |
style.css.json | ||
---|---|---|
@@ -1,0 +1,1 @@ | ||
1 … | +".hyperscroll {\n width: 100%;\n}\n\n.hyperscroll > .content {\n max-width: 680px;\n margin-left: auto;\n margin-right: auto;\n}\n\n\n.message {\n display: block;\n margin-top: .25em;\n margin-bottom: .25em;\n}\n" |
style.js | ||
---|---|---|
@@ -1,0 +1,8 @@ | ||
1 … | +var fs = require('fs') | |
2 … | +var path = require('path') | |
3 … | + | |
4 … | +fs.writeFileSync( | |
5 … | + path.join(__dirname, 'style.css.json'), | |
6 … | + JSON.stringify(fs.readFileSync(path.join(__dirname, 'style.css'), 'utf8')) | |
7 … | +) | |
8 … | + |
views.js | ||
---|---|---|
@@ -1,0 +1,71 @@ | ||
1 … | +var pull = require('pull-stream') | |
2 … | +var sbot = require('./scuttlebot') | |
3 … | +var hyperscroll = require('hyperscroll') | |
4 … | +var More = require('pull-more') | |
5 … | +var stream = require('hyperloadmore/stream') | |
6 … | +var h = require('hyperscript') | |
7 … | +var render = require('./render') | |
8 … | + | |
9 … | +module.exports.logstream = function () { | |
10 … | + var content = h('div.content') | |
11 … | + | |
12 … | + document.body.appendChild(h('div.screen', | |
13 … | + {style: {position: 'absolute', top: '0px', bottom: '0px', left: '0px', right: '0px'}}, | |
14 … | + hyperscroll(content) | |
15 … | + )) | |
16 … | + | |
17 … | + function createStream (opts) { | |
18 … | + return pull( | |
19 … | + More(sbot.createLogStream, opts), | |
20 … | + pull.filter(function (data) { | |
21 … | + return 'string' === typeof data.value.content.text | |
22 … | + }), | |
23 … | + pull.map(function (msg) { | |
24 … | + return h('div.message', render(msg)) | |
25 … | + }) | |
26 … | + ) | |
27 … | + } | |
28 … | + | |
29 … | + pull( | |
30 … | + createStream({old: false, limit: 100}), | |
31 … | + stream.top(content) | |
32 … | + ) | |
33 … | + | |
34 … | + pull( | |
35 … | + createStream({reverse: true, live: false, limit: 100}), | |
36 … | + stream.bottom(content) | |
37 … | + ) | |
38 … | +} | |
39 … | + | |
40 … | +var rawJSON = require('patchapp-raw/json') | |
41 … | + | |
42 … | +module.exports.rawstream = function () { | |
43 … | + var content = h('div.content') | |
44 … | + | |
45 … | + document.body.appendChild(h('div.screen', | |
46 … | + {style: {position: 'absolute', top: '0px', bottom: '0px', left: '0px', right: '0px'}}, | |
47 … | + hyperscroll(content) | |
48 … | + )) | |
49 … | + | |
50 … | + function createStream (opts) { | |
51 … | + return pull( | |
52 … | + More(sbot.createLogStream, opts), | |
53 … | + pull.filter(function (data) { | |
54 … | + return 'string' === typeof data.value.content.text | |
55 … | + }), | |
56 … | + pull.map(function (msg) { | |
57 … | + return h('pre.raw__json', {id: msg.key}, rawJSON(msg)) | |
58 … | + }) | |
59 … | + ) | |
60 … | + } | |
61 … | + | |
62 … | + pull( | |
63 … | + createStream({old: false, limit: 10}), | |
64 … | + stream.top(content) | |
65 … | + ) | |
66 … | + | |
67 … | + pull( | |
68 … | + createStream({reverse: true, live: false, limit: 10}), | |
69 … | + stream.bottom(content) | |
70 … | + ) | |
71 … | +} |
Built with git-ssb-web