git ssb

2+

dinoworm 🐛 / ssb-graphviz



Commit ca718f616a051e93adeb22faa3fc06646f667973

re-write everything, just because, not yet working

Michael Williams committed on 12/9/2016, 7:13:05 AM
Parent: 4c04551a3e25f880780438f3d6ba6a54f100d610

Files changed

index.jschanged
package.jsonchanged
graph.jsdeleted
api.jsadded
app.jsadded
output.jsdeleted
assets/api.jsadded
browser.jsadded
renderer.jsdeleted
client.jsadded
config.jsadded
graph/actions.jsadded
graph/api.jsadded
graph/app.jsadded
graph/helpers/build-links.jsadded
graph/helpers/build-nodes.jsadded
graph/view.jsadded
profiles/actions.jsadded
profiles/api.jsadded
profiles/app.jsadded
profiles/view.jsadded
server.jsadded
index.jsView
@@ -1,34 +1,61 @@
1 +const http = require('http')
2 +const Assets = require('bankai')
3 +const { join } = require('path')
14 const fromJson = require('ngraph.fromjson')
2-const Renderer = require('./renderer')
3-const Graph = require('./graph')
5 +const defaultsDeep = require('lodash/defaultsDeep')
46
5-const config = {
6- physics: {
7- springLength: 80,
8- springCoeff: 0.0001,
9- gravity: -1.4,
10- theta: 0.4,
11- dragCoeff: 0.04
12- },
13- link: (link) => {
14- // if (link.data.hidden) return
15- // makes linkUI element not exist ? => display.getLink doesn't work
7 +const defaultVizConfig = require('./config')
8 +const Api = require('./api')
169
17- return {
18- fromColor: 0x000066,
19- toColor: 0x000066
20- }
10 +var _server
11 +
12 +module.exports = {
13 + name: 'ssb-graphviz',
14 + version: require('./package.json').version,
15 + manifest: {},
16 + init: function (ssb, config, reconnect) {
17 + // close existing server. when scuttlebot plugins get a deinit method, we
18 + // will close it in that instead it
19 + if (server) server.close()
20 +
21 + var server = Server(ssb, config, reconnect)
22 + _server = server
23 + server.listen()
24 +
25 + return {}
2126 }
2227 }
2328
24-module.exports = function (sbot, cb) {
25- Graph(sbot, (err, data) => {
26- if (err) return cb(err)
27- const str = JSON.stringify(data)
28- var graph = fromJson(str)
29- var display = Renderer(graph, config, sbot)
29 +function Server (ssb, config, reconnect) {
30 + const vizConfig = defaultsDeep(config['ssb-graphviz'], defaultVizConfig)
31 + const { host, port } = parseAddr(config.listenAddr, {
32 + host: vizConfig.host,
33 + port: vizConfig.port
34 + })
3035
31- cb(null, display)
32- })
36 + var server = http.createServer(Api(ssb, config))
37 +
38 + return {
39 + listen,
40 + close
41 + }
42 +
43 + function listen () {
44 + server.listen(port, host, function () {
45 + var hostName = ~host.indexOf(':') ? '[' + host + ']' : host
46 + console.log(`Listening on http://${hostName}:${port}/`)
47 + })
48 + }
49 +
50 + function close () {
51 + server.close()
52 + }
3353 }
3454
55 +function parseAddr(str, def) {
56 + if (!str) return def
57 + var i = str.lastIndexOf(':')
58 + if (~i) return {host: str.substr(0, i), port: str.substr(i+1)}
59 + if (isNaN(str)) return {host: str, port: def.port}
60 + return {host: def.host, port: str}
61 +}
package.jsonView
@@ -4,9 +4,10 @@
44 "description": "visualize the ssb network graph",
55 "main": "index.js",
66 "scripts": {
77 "test": "standard",
8- "start": "electro output.js"
8 + "start": "node server",
9 + "start:dev": "node-dev server"
910 },
1011 "repository": {
1112 "type": "git",
1213 "url": " ssb://%hNm67sfnZFtWkD/+1qxH3UfzhXykfpKtOL1C/XbLANA=.sha256"
@@ -18,23 +19,30 @@
1819 "url": "https://git.scuttlebot.io/%25hNm67sfnZFtWkD%2F%2B1qxH3UfzhXykfpKtOL1C%2FXbLANA%3D.sha256/issues"
1920 },
2021 "homepage": "https://git.scuttlebot.io/%25hNm67sfnZFtWkD%2F%2B1qxH3UfzhXykfpKtOL1C%2FXbLANA%3D.sha256#readme",
2122 "devDependencies": {
23 + "inu-log": "^1.0.2",
24 + "node-dev": "^3.1.3",
2225 "standard": "^8.6.0",
2326 "tape": "^4.5.1"
2427 },
2528 "dependencies": {
26- "electro": "^2.0.2",
27- "electron": "^1.4.4",
28- "hyperscript": "^2.0.2",
29- "insert-css": "^1.0.0",
29 + "bankai": "^5.1.3",
30 + "cache-element": "^2.0.0",
3031 "inu": "^3.1.3",
31- "ngraph.fromjson": "^0.1.8",
32- "ngraph.pixel": "^2.2.0",
32 + "inux": "^2.1.0",
33 + "lodash": "^4.17.2",
34 + "ngraph.pixel": "github:ahdinosaur/ngraph.pixel#sheetify-compatible-styles",
35 + "pull-async": "^1.0.0",
3336 "pull-stream": "^3.4.5",
3437 "run-waterfall": "^1.1.3",
38 + "send-data": "^8.0.0",
3539 "ssb-avatar": "^0.2.0",
3640 "ssb-client": "^4.3.0",
41 + "ssb-config": "^2.2.0",
42 + "ssb-keys": "^7.0.3",
43 + "ssb-reconnect": "^0.1.1",
44 + "stack": "^0.1.0",
3745 "stream-to-pull-stream": "^1.7.2",
38- "yo-yo": "^1.3.1"
46 + "xhr": "^2.3.1"
3947 }
4048 }
graph.jsView
@@ -1,67 +1,0 @@
1-const waterfall = require('run-waterfall')
2-
3-const { keys } = Object
4-
5-module.exports = Graph
6-
7-function Graph (sbot, cb) {
8- waterfall([
9- (cb) => sbot.friends.all(cb),
10- (friends, cb) => {
11- const filteredFriends = friends // activeFriends(friends)
12-
13- cb(null, {
14- nodes: buildNodes(filteredFriends),
15- links: buildLinks(filteredFriends)
16- })
17- }
18- ], cb)
19-}
20-
21-function buildNodes (friends) {
22- return keys(friends).map(id => {
23- return {
24- id,
25- data: {
26- friends: keys(friends[id])
27- }
28- }
29- })
30-}
31-
32-function buildLinks (friends) {
33- return keys(friends).reduce((sofar, friend) => {
34- const friendOfFriends = keys(friends[friend])
35- .filter(id => friends[friend][id] === true)
36-
37- const edges = friendOfFriends.map(friendOfFriend => {
38- return {
39- fromId: friend,
40- toId: friendOfFriend,
41- data: {
42- hidden: true
43- }
44- }
45- })
46-
47- return [
48- ...sofar,
49- ...edges
50- ]
51- }, [])
52-}
53-
54-/*
55-function activeFriends (friends) {
56- return keys(friends)
57- .reduce(
58- (sofar, current) => {
59- if (keys(friends[current]).length < 4) return sofar
60-
61- sofar[current] = friends[current]
62- return sofar
63- },
64- {}
65- )
66-}
67-*/
api.jsView
@@ -1,0 +1,11 @@
1 +const Stack = require('stack')
2 +
3 +module.exports = VizApi
4 +
5 +function VizApi (ssb, config) {
6 + return Stack.apply(null, [
7 + require('./graph/api')(ssb, config),
8 + require('./profiles/api')(ssb, config),
9 + require('./assets/api')(ssb, config),
10 + ])
11 +}
app.jsView
@@ -1,0 +1,9 @@
1 +const { App } = require('inux')
2 +
3 +module.exports = VizApp
4 +
5 +function VizApp (config) {
6 + return App([
7 + require('./graph/app')(config)
8 + ])
9 +}
output.jsView
@@ -1,31 +1,0 @@
1-const insertCss = require('insert-css')
2-const Sbot = require('ssb-client')
3-const waterfall = require('run-waterfall')
4-
5-const Viz = require('./')
6-
7-insertCss(`
8- html, body {
9- width: 100%;
10- height: 100%;
11- position: absolute;
12- overflow: hidden;
13- padding: 0;
14- margin: 0;
15- }
16-
17- .avatar {
18- position: fixed;
19- left: 10px;
20- bottom: 10px;
21- color: #fff;
22- }
23-
24- .avatar .image {
25- max-height: 160px;
26- }
27-`)
28-
29-waterfall([Sbot, Viz], (err) => {
30- if (err) throw err
31-})
assets/api.jsView
@@ -1,0 +1,37 @@
1 +const { join } = require('path')
2 +const sendJson = require('send-data/json')
3 +const sendError = require('send-data/error')
4 +const pull = require('pull-stream')
5 +const toPull = require('stream-to-pull-stream')
6 +const Assets = require('bankai')
7 +
8 +module.exports = AssetsApi
9 +
10 +function AssetsApi (ssb, config) {
11 + const clientPath = join(__dirname, '../browser.js')
12 + const assets = Assets(clientPath)
13 +
14 + return (req, res, next) => {
15 + if (req.url === '/') {
16 + assets.html(req, res).pipe(res)
17 + } else if (req.url.substring(0, 7) === '/blobs/') {
18 + const id = req.url.substring(7)
19 + sendBlob(id, req, res)
20 + } else if (req.url === '/bundle.js') {
21 + assets.js(req, res).pipe(res)
22 + } else if (req.url === '/bundle.css') {
23 + assets.css(req, res).pipe(res)
24 + } else next()
25 + }
26 +
27 + function sendBlob (id, req, res) {
28 + pull(
29 + ssb.blobs.get(id),
30 + toPull(res)
31 + )
32 + }
33 +
34 + function getBlob (id) {
35 + return ssb.blobs.get(id)
36 + }
37 +}
browser.jsView
@@ -1,0 +1,33 @@
1 +const css = require('sheetify')
2 +const { start, html, pull } = require('inu')
3 +const log = require('inu-log')
4 +
5 +const config = require('./config')
6 +const App = require('./app')
7 +
8 +css`
9 + html, body, .main, .graph {
10 + width: 100%;
11 + height: 100%;
12 + position: absolute;
13 + overflow: hidden;
14 + padding: 0;
15 + margin: 0;
16 + }
17 +`
18 +
19 +var app = App(config)
20 +if (process.env.NODE_ENV !== 'production') {
21 + app = log(app)
22 +}
23 +const { views, models } = start(app)
24 +const main = document.createElement('div')
25 +main.className = 'main'
26 +document.body.appendChild(main)
27 +
28 +pull(
29 + views(),
30 + pull.drain(view => {
31 + html.update(main, view)
32 + })
33 +)
renderer.jsView
@@ -1,72 +1,0 @@
1-const Renderer = require('ngraph.pixel')
2-const avatar = require('ssb-avatar')
3-const html = require('yo-yo')
4-
5-module.exports = createRenderer
6-
7-function createRenderer (graph, config, sbot) {
8- var avatarEl = html`<div class="avatar" />`
9- document.body.appendChild(avatarEl)
10-
11- var display = Renderer(graph, config)
12-
13- display.on('nodehover', handleNodeHover)
14-
15- return display
16-
17- function handleNodeHover (node) {
18- if (node === undefined) return
19-
20- avatar(sbot, node.id, node.id, (err, { name, image }) => {
21- // handle this error!
22- if (err) throw err
23-
24- const imgSrc = image ? `http://localhost:7777/${image}` : ''
25-
26- const newAvatarEl = html`
27- <div class="avatar">
28- <img class="image" src=${imgSrc} />
29- <div class="name">${name}</div>
30- </div>
31- `
32-
33- html.update(avatarEl, newAvatarEl)
34- })
35-
36- display.forEachLink(linkUI => {
37- const { from, to } = linkUI
38- const friends = node.data.friends
39-
40- const isFromTarget = node.id === from.id
41- const isToTarget = node.id === to.id
42-
43- const isFromFriend = friends.indexOf(from.id) > -1
44- const isToFriend = friends.indexOf(to.id) > -1
45- const involvesFoaF = isFromFriend || isToTarget
46-
47- var fromColor = 0x000066
48- var toColor = 0x000066
49-
50- const close = 0xffffff
51- const mid = 0xa94caf
52- const far = 0x000066
53-
54- if (isFromTarget) {
55- fromColor = close
56- toColor = mid
57- } else if (isToTarget) {
58- fromColor = mid
59- toColor = close
60- } else if (involvesFoaF && isFromFriend) {
61- fromColor = mid
62- toColor = far
63- } else if (involvesFoaF && isToFriend) {
64- fromColor = far
65- toColor = mid
66- }
67-
68- linkUI.fromColor = fromColor
69- linkUI.toColor = toColor
70- })
71- }
72-}
client.jsView
@@ -1,0 +1,17 @@
1 +const xhr = require('xhr')
2 +const inu =
3 +
4 +module.exports = {
5 + graph: () {
6 +
7 + }
8 +}
9 + Graph(ssb, (err, data) => {
10 + if (err) return cb(err)
11 + const str = JSON.stringify(data)
12 + var graph = fromJson(str)
13 + var display = Renderer(graph, config, sbot)
14 +
15 + cb(null, display)
16 + })
17 +
config.jsView
@@ -1,0 +1,22 @@
1 +const config = {
2 + host: 'localhost',
3 + port: 7781,
4 + physics: {
5 + springLength: 80,
6 + springCoeff: 0.0001,
7 + gravity: -1.4,
8 + theta: 0.4,
9 + dragCoeff: 0.04
10 + },
11 + link: (link) => {
12 + // if (link.data.hidden) return
13 + // makes linkUI element not exist ? => display.getLink doesn't work
14 +
15 + return {
16 + fromColor: 0x000066,
17 + toColor: 0x000066
18 + }
19 + }
20 +}
21 +
22 +module.exports = config
graph/actions.jsView
@@ -1,0 +1,22 @@
1 +const { Action } = require('inux')
2 +
3 +const SET = Symbol('set')
4 +const SET_FOCUS = Symbol('setFocus')
5 +const FETCH = Symbol('fetch')
6 +const FOCUS = Symbol('focus')
7 +
8 +const set = Action(SET)
9 +const setFocus = Action(SET_FOCUS)
10 +const fetch = Action(FETCH)
11 +const focus = Action(FOCUS)
12 +
13 +module.exports = {
14 + SET,
15 + SET_FOCUS,
16 + FETCH,
17 + FOCUS,
18 + set,
19 + setFocus,
20 + fetch,
21 + focus
22 +}
graph/api.jsView
@@ -1,0 +1,42 @@
1 +const waterfall = require('run-waterfall')
2 +const sendJson = require('send-data/json')
3 +const sendError = require('send-data/error')
4 +
5 +const buildLinks = require('./helpers/build-links')
6 +const buildNodes = require('./helpers/build-nodes')
7 +
8 +module.exports = GraphApi
9 +
10 +function GraphApi (ssb, config) {
11 + return (req, res, next) => {
12 + switch (req.url) {
13 + case '/api/graph':
14 + sendGraph(req, res)
15 + break;
16 + default:
17 + next()
18 + }
19 + }
20 +
21 + function sendGraph (req, res) {
22 + getGraph((err, graph) => {
23 + if (err) {
24 + sendError(req, res, { body: err })
25 + } else {
26 + sendJson(req, res, { body: graph })
27 + }
28 + })
29 + }
30 +
31 + function getGraph (cb) {
32 + waterfall([
33 + ssb.friends.all,
34 + (friends, cb) => {
35 + cb(null, {
36 + nodes: buildNodes(friends),
37 + links: buildLinks(friends)
38 + })
39 + }
40 + ], cb)
41 + }
42 +}
graph/app.jsView
@@ -1,0 +1,66 @@
1 +const xhr = require('xhr')
2 +const { Domain } = require('inux')
3 +const async = require('pull-async')
4 +const html = require('inu/html')
5 +
6 +const { SET, SET_FOCUS, FETCH, FOCUS, set, setFocus, fetch } = require('./actions')
7 +const GraphView = require('./view')
8 +const { fetchOne: fetchProfile } = require('../profiles/actions')
9 +const ProfileView = require('../profiles/view')
10 +
11 +module.exports = GraphApp
12 +
13 +function GraphApp (config) {
14 + const graphView = GraphView(config)
15 + const profileView = ProfileView(config)
16 +
17 + return Domain({
18 + name: 'graph',
19 + init: () => ({
20 + model: {
21 + nodes: [],
22 + links: []
23 + },
24 + effect: fetch()
25 + }),
26 + update: {
27 + [SET]: (model, graph) => ({ model: graph }),
28 + [SET_FOCUS]: (model, focus) => ({
29 + model: assign({}, model, { focus })
30 + })
31 + },
32 + run: {
33 + [FETCH]: () => {
34 + return async(cb => {
35 + xhr({
36 + url: '/api/graph',
37 + json: true
38 + }, (err, resp, { body } = {}) => {
39 + if (err) return cb(err)
40 + cb(null, set(body))
41 + })
42 + })
43 + },
44 + [FOCUS]: (id) => {
45 + return pull.values([
46 + setFocus(id),
47 + fetchProfile(id)
48 + ])
49 + }
50 + },
51 + routes: [
52 + ['/', (params, model, dispatch) => {
53 + const { graph, profiles } = model
54 + const { focus } = graph
55 + const focusedProfile = focus ? profiles[focus] : undefined
56 +
57 + return html`
58 + <div class='main'>
59 + ${profileView(focusedProfile, dispatch)}
60 + ${graphView(graph, dispatch)}
61 + </div>
62 + `
63 + }]
64 + ]
65 + })
66 +}
graph/helpers/build-links.jsView
@@ -1,0 +1,25 @@
1 +const { keys } = Object
2 +
3 +module.exports = buildLinks
4 +
5 +function buildLinks (friends) {
6 + return keys(friends).reduce((sofar, friend) => {
7 + const friendOfFriends = keys(friends[friend])
8 + .filter(id => friends[friend][id] === true)
9 +
10 + const edges = friendOfFriends.map(friendOfFriend => {
11 + return {
12 + fromId: friend,
13 + toId: friendOfFriend,
14 + data: {
15 + hidden: true
16 + }
17 + }
18 + })
19 +
20 + return [
21 + ...sofar,
22 + ...edges
23 + ]
24 + }, [])
25 +}
graph/helpers/build-nodes.jsView
@@ -1,0 +1,15 @@
1 +const { keys } = Object
2 +
3 +module.exports = buildNodes
4 +
5 +function buildNodes (friends) {
6 + return keys(friends).map(id => {
7 + return {
8 + id,
9 + data: {
10 + friends: keys(friends[id])
11 + }
12 + }
13 + })
14 +}
15 +
graph/view.jsView
@@ -1,0 +1,91 @@
1 +const { assign } = Object
2 +const Ngraph = require('ngraph.graph')
3 +const Renderer = require('ngraph.pixel')
4 +const html = require('inu/html')
5 +const Widget = require('cache-element/widget')
6 +
7 +const { focusNode } = require('./actions')
8 +
9 +module.exports = GraphView
10 +
11 +function GraphView (config) {
12 + var ngraph = Ngraph()
13 + var dispatch
14 +
15 + return Widget({
16 + render: function (graph, dispatch) {
17 + return html`<div class='graph'></div>`
18 + },
19 + onupdate: function (el, graph, _dispatch) {
20 + updateGraph(graph)
21 + dispatch = _dispatch
22 + },
23 + onload: initGraph
24 + })
25 +
26 + function updateGraph ({ nodes, links }) {
27 + ngraph.beginUpdate()
28 + ngraph.forEachLink(link => {
29 + ngraph.removeLink(link)
30 + })
31 + ngraph.forEachNode(node => {
32 + ngraph.removeNode(node)
33 + })
34 + nodes.forEach(node => {
35 + ngraph.addNode(node.id, node.data)
36 + })
37 + links.forEach(link => {
38 + ngraph.addLink(link.fromId, link.toId, link.data)
39 + })
40 + ngraph.endUpdate()
41 + }
42 +
43 + function initGraph (node) {
44 + const ngraphConfig = assign({ container: node }, config)
45 + var display = Renderer(ngraph, ngraphConfig)
46 +
47 + display.on('nodehover', handleNodeHover)
48 +
49 + function handleNodeHover (node) {
50 + if (node === undefined) return
51 +
52 + dispatch(focusProfile(node.id))
53 +
54 + display.forEachLink(linkUI => {
55 + const { from, to } = linkUI
56 + const friends = node.data.friends
57 +
58 + const isFromTarget = node.id === from.id
59 + const isToTarget = node.id === to.id
60 +
61 + const isFromFriend = friends.indexOf(from.id) > -1
62 + const isToFriend = friends.indexOf(to.id) > -1
63 + const involvesFoaF = isFromFriend || isToTarget
64 +
65 + var fromColor = 0x000066
66 + var toColor = 0x000066
67 +
68 + const close = 0xffffff
69 + const mid = 0xa94caf
70 + const far = 0x000066
71 +
72 + if (isFromTarget) {
73 + fromColor = close
74 + toColor = mid
75 + } else if (isToTarget) {
76 + fromColor = mid
77 + toColor = close
78 + } else if (involvesFoaF && isFromFriend) {
79 + fromColor = mid
80 + toColor = far
81 + } else if (involvesFoaF && isToFriend) {
82 + fromColor = far
83 + toColor = mid
84 + }
85 +
86 + linkUI.fromColor = fromColor
87 + linkUI.toColor = toColor
88 + })
89 + }
90 + }
91 +}
profiles/actions.jsView
@@ -1,0 +1,14 @@
1 +const { Action } = require('inux')
2 +
3 +const SET_ONE = Symbol('setOne')
4 +const FETCH_ONE = Symbol('fetchOne')
5 +
6 +const setOne = Action(SET_ONE)
7 +const fetchOne = Action(FETCH_ONE)
8 +
9 +module.exports = {
10 + SET_ONE,
11 + FETCH_ONE,
12 + setOne,
13 + fetchOne
14 +}
profiles/api.jsView
@@ -1,0 +1,32 @@
1 +const Avatar = require('ssb-avatar')
2 +const sendJson = require('send-data/json')
3 +const sendError = require('send-data/error')
4 +
5 +module.exports = ProfilesApi
6 +
7 +function ProfilesApi (ssb, config) {
8 + return (req, res, next) => {
9 + if (req.url.substring(0, 15) === '/api/profiles/') {
10 + const id = req.url.substring(16)
11 + sendProfile(id, req, res)
12 + } else next()
13 + }
14 +
15 + function sendProfile (id, req, res) {
16 + getProfile(id, (err, profile) => {
17 + if (err) {
18 + sendError(req, res, { body: err })
19 + } else {
20 + sendJson(req, res, { body: profile })
21 + }
22 + })
23 + }
24 +
25 + function getProfile (id, cb) {
26 + Avatar(ssb, id, id, (err, { name, image } = {}) => {
27 + if (err) return cb(err)
28 + const imgSrc = image ? `/images/${image}` : ''
29 + cb(null, { id, name, image })
30 + })
31 + }
32 +}
profiles/app.jsView
@@ -1,0 +1,32 @@
1 +const { assign } = Object
2 +const async = require('pull-async')
3 +
4 +const { SET_ONE, FETCH_ONE, setOne } = require('./actions')
5 +
6 +module.exports = ProfilesApp
7 +
8 +function ProfilesApp (config) {
9 + return {
10 + init: () => ({
11 + model: {},
12 + }),
13 + update: {
14 + [SET_ONE]: (model, profile) => ({
15 + model: assign({}, model, { [profile.id]: profile })
16 + })
17 + },
18 + run: {
19 + [FETCH_ONE]: (id) => {
20 + return async(cb => {
21 + xhr({
22 + url: `/api/profiles/${profile}`,
23 + json: true
24 + }, (err, resp, { body } = {}) => {
25 + if (err) return cb(err)
26 + cb(null, setOne(body))
27 + })
28 + })
29 + },
30 + }
31 + }
32 +}
profiles/view.jsView
@@ -1,0 +1,31 @@
1 +const html = require('inu/html')
2 +const css = require('sheetify')
3 +
4 +module.exports = ProfileView
5 +
6 +css`
7 + .avatar {
8 + position: fixed;
9 + left: 10px;
10 + bottom: 10px;
11 + color: #fff;
12 + }
13 +
14 + .avatar .image {
15 + max-height: 160px;
16 + }
17 +`
18 +
19 +function ProfileView (config) {
20 + return (profile, dispatch) => {
21 + if (profile === undefined) return
22 + const { name, image } = profile
23 +
24 + return html`
25 + <div class="profile">
26 + <img class="image" src=${image} />
27 + <div class="name">${name}</div>
28 + </div>
29 + `
30 + }
31 +}
server.jsView
@@ -1,0 +1,21 @@
1 +#!/bin/sh
2 +':' //; exec "$(command -v node || command -v nodejs)" "$0" "$@"
3 +// http://unix.stackexchange.com/questions/65235/universal-node-js-shebang
4 +// vi: ft=javascript
5 +
6 +const appName = process.env.appName
7 +const config = require('ssb-config/inject')(appName)
8 +const ssbClient = require('ssb-client')
9 +const keys = require('ssb-keys')
10 + .loadOrCreateSync(require('path').join(config.path, 'secret'))
11 +const Viz = require('.')
12 +
13 +config.listenAddr = config._[1]
14 +config.appName = appName
15 +
16 +require('ssb-reconnect')(function (cb) {
17 + ssbClient(keys, config, cb)
18 +}, function (err, ssb, reconnect) {
19 + if (err) throw err
20 + Viz.init(ssb, config, reconnect)
21 +})

Built with git-ssb-web