git ssb

16+

Dominic / patchbay



Tree: da787189e650087caa476419997849d890f75995

Files: da787189e650087caa476419997849d890f75995 / modules_extra / network.js

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

Built with git-ssb-web