git ssb

16+

Dominic / patchbay



Tree: f93ceefd06a4ef69e3af1d2fb39bc0fed84c2c7a

Files: f93ceefd06a4ef69e3af1d2fb39bc0fed84c2c7a / modules_extra / network.js

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

Built with git-ssb-web