git ssb

16+

Dominic / patchbay



Tree: 62404afd51be20fc4b7925c7ad3b91a4a4b34915

Files: 62404afd51be20fc4b7925c7ad3b91a4a4b34915 / modules_extra / network.js

6257 bytesRaw
1const fs = require('fs')
2// const { isVisible } = require('is-visible')
3const h = require('../h')
4const human = require('human-time')
5
6const Struct = require('@mmckegg/mutant/struct')
7const Value = require('@mmckegg/mutant/value')
8const Dict = require('@mmckegg/mutant/dict')
9const toCollection = require('@mmckegg/mutant/dict-to-collection')
10const mutantMap = require('@mmckegg/mutant/map')
11const when = require('@mmckegg/mutant/when')
12const computed = require('@mmckegg/mutant/computed')
13
14//var avatar = plugs.first(exports.avatar = [])
15//var sbot_gossip_peers = plugs.first(exports.sbot_gossip_peers = [])
16//var sbot_gossip_connect = plugs.first(exports.sbot_gossip_connect = [])
17
18exports.needs = {
19 avatar_image_link: 'first',
20 avatar_name_link: 'first',
21 sbot_gossip_peers: 'first',
22 sbot_gossip_connect: 'first'
23}
24
25exports.gives = {
26 menu_items: true,
27 builtin_tabs: true,
28 screen_view: true,
29 mcss: true
30}
31
32//sbot_gossip_connect
33//sbot_gossip_add
34
35
36function legacyToMultiServer(addr) {
37 return 'net:'+addr.host + ':'+addr.port + '~shs:'+addr.key.substring(1).replace('.ed25519','')
38}
39
40//on the same wifi network
41function isLocal (peer) {
42 // don't rely on private ip address, because
43 // cjdns creates fake private ip addresses.
44 return ip.isPrivate(peer.host) && peer.type === 'local'
45}
46
47
48function getType (peer) {
49 return (
50 isLongterm(peer) ? 'modern'
51 : isLegacy(peer) ? 'legacy'
52 : isInactive(peer) ? 'inactive'
53 : isUnattempted(peer) ? 'unattempted'
54 : 'other' //should never happen
55 )
56
57 //pub is running scuttlebot >=8
58 //have connected successfully.
59 function isLongterm (peer) {
60 return peer.ping && peer.ping.rtt && peer.ping.rtt.mean > 0
61 }
62
63 //pub is running scuttlebot < 8
64 //have connected sucessfully
65 function isLegacy (peer) {
66 return /connect/.test(peer.state) || (peer.duration && peer.duration.mean) > 0 && !isLongterm(peer)
67 }
68
69 //tried to connect, but failed.
70 function isInactive (peer) {
71 return peer.stateChange && (peer.duration && peer.duration.mean == 0)
72 }
73
74 //havn't tried to connect peer yet.
75 function isUnattempted (peer) {
76 return !peer.stateChange
77 }
78}
79
80function origin (peer) {
81 return peer.source === 'local' ? 0 : 1
82}
83
84function round(n) {
85 return Math.round(n*100)/100
86}
87
88function duration (s) {
89 if(!s) return s
90 if (Math.abs(s) > 30000)
91 return round(s/60000)+'m'
92 else if (Math.abs(s) > 500)
93 return round(s/1000)+'s'
94 else
95 return round(s)+'ms'
96}
97
98function peerListSort (a, b) {
99 var states = {
100 connected: 3,
101 connecting: 2
102 }
103
104 //types of peers
105 var types = {
106 modern: 4,
107 legacy: 3,
108 inactive: 2,
109 unattempted: 1,
110 other: 0
111 }
112
113 return (
114 (states[b.state] || 0) - (states[a.state] || 0)
115 || origin(b) - origin(a)
116 || types[getType(b)] - types[getType(a)]
117 || b.stateChange - a.stateChange
118 )
119}
120
121function formatDate (time) {
122 return new Date(time).toString()
123}
124
125function humanDate (time) {
126 return human(new Date(time)).replace(/minute/, 'min').replace(/second/, 'sec')
127}
128
129exports.create = function (api) {
130
131 return {
132 menu_items: () => h('a', {href: '#/network'}, '/network'),
133 builtin_tabs: () => ['/network'],
134 screen_view,
135 mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8')
136 }
137
138 function screen_view (path) {
139 if (path !== '/network') return
140
141 var peers = obs_gossip_peers(api)
142
143 return h('div', { style: {'overflow':'auto'}, className: 'column scroller' }, [
144 h('Network', [
145 mutantMap(peers, peer => {
146 var { key, ping, source, state, stateChange } = peer
147
148 return h('NetworkConnection', [
149 h('section.avatar', [
150 api.avatar_image_link(key()),
151 ]),
152 h('section.name', [
153 api.avatar_name_link(key()),
154 ]),
155 h('section.type', [
156 computed(peer, getType),
157 ]),
158 h('section.source', [
159 h('label', 'source:'),
160 h('code', source)
161 ]),
162 h('section.state', [
163 h('label', 'state:'),
164 h('i', {
165 className: computed(state, (state) => '-'+state)
166 }),
167 h('code', when(state, state, 'not connected'))
168 ]),
169 h('section.actions', [
170 h('button', {
171 'ev-click': () => {
172 api.sbot_gossip_connect(peer(), (err) => {
173 if(err) console.error(err)
174 else console.log('connected to', peer())
175 })
176 }},
177 'connect'
178 )
179 ]),
180 h('section.time-ago', [
181 h('div',
182 { title: computed(stateChange, formatDate) },
183 [ computed(stateChange, humanDate) ]
184 )
185 ]),
186 h('section.ping', [
187 h('div.rtt', [
188 h('label', 'rtt:'),
189 h('code', computed(ping.rtt.mean, duration))
190 ]),
191 h('div.skew', [
192 h('label', 'skew:'),
193 h('code', computed(ping.skew.mean, duration))
194 ]),
195 ]),
196 h('section.address', [
197 h('code', computed(peer, legacyToMultiServer))
198 ])
199 ])
200 })
201 ])
202 ])
203 }
204}
205
206function obs_gossip_peers (api) {
207 var timer = null
208 var state = Dict({}, {
209 onListen: () => {
210 timer = setInterval(refresh, 5e3)
211 },
212 onUnlisten: () => {
213 clearInterval(timer)
214 }
215 })
216
217 refresh()
218
219 return toCollection.values(state)
220
221 function refresh () {
222 api.sbot_gossip_peers((err, peers) => {
223 peers.forEach(data => {
224 var current = state.get(data.key)
225 if (!current) {
226 current = Peer()
227 current.set(data)
228 state.put(data.key, current)
229 } else {
230 current.set(data)
231 }
232 })
233 })
234 }
235}
236
237function Peer () {
238 var peer = Struct({
239 key: Value(),
240 ping: Struct({
241 rtt: Struct({
242 mean: Value()
243 }),
244 skew: Struct({
245 mean: Value()
246 })
247 }),
248 source: Value(),
249 state: Value(),
250 stateChange: Value()
251 })
252
253 return peer
254}
255
256

Built with git-ssb-web