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