main
tsx 173 lines 5.15 KB
Raw
1 import { useEffect, useState } from 'react'
2 import libp2pLogo from '../../public/img/libp2p.svg'
3 import { ConnectingPanel } from './connecting.tsx'
4 import { Debug } from './debug.js'
5 import { FatalErrorPanel } from './fatal-error.tsx'
6 import { Identify } from './identify.tsx'
7 import { Menu } from './menu.js'
8 import { Node } from './node.js'
9 import { Peers } from './peers/index.js'
10 import { Ping } from './ping.tsx'
11 import { PubSub } from './pubsub/index.js'
12 import { Routing } from './routing/index.js'
13 import type { InspectorRPCEvents, MetricsRPC, Peer, PeerAddress } from '@ipshipyard/libp2p-inspector-metrics'
14 import type { PeerId, TypedEventTarget } from '@libp2p/interface'
15 import type { ReactElement } from 'react'
16
17 export interface InspectorProps {
18 metrics: MetricsRPC
19 events: TypedEventTarget<InspectorRPCEvents>
20 }
21
22 export function Inspector ({ metrics, events }: InspectorProps): ReactElement {
23 const panels = ['Node', 'Peers', 'Debug', 'Routing']
24
25 const [id, setId] = useState<PeerId>()
26 const [addresses, setAddresses] = useState<PeerAddress[]>([])
27 const [protocols, setProtocols] = useState<string[]>([])
28 const [metadata, setMetadata] = useState<Record<string, string>>({})
29 const [peers, setPeers] = useState<Peer[]>([])
30 const [debug, setDebug] = useState('')
31 const [capabilities, setCapabilities] = useState<Record<string, string[]>>({})
32 const [error, setError] = useState<Error>()
33 const [panel, setPanel] = useState(panels[0])
34
35 useEffect(() => {
36 Promise.resolve()
37 .then(async () => {
38 const { self, peers, debug, capabilities } = await metrics.init({
39 signal: AbortSignal.timeout(1_000)
40 })
41
42 setId(self.id)
43 setAddresses(self.addresses)
44 setProtocols(self.protocols)
45 setMetadata(self.metadata)
46 setPeers(peers)
47 setDebug(debug)
48 setCapabilities(capabilities)
49 })
50 .catch(err => {
51 setError(err)
52 })
53
54 function onMetrics (evt: CustomEvent<Record<string, any>>): void {
55
56 }
57
58 function onSelf (evt: CustomEvent<Peer>): void {
59 if (!evt.detail.id.equals(id)) {
60 setId(evt.detail.id)
61 }
62
63 if (JSON.stringify(evt.detail.addresses) !== JSON.stringify(addresses)) {
64 setAddresses(evt.detail.addresses)
65 }
66
67 if (JSON.stringify(evt.detail.protocols) !== JSON.stringify(protocols)) {
68 setProtocols(evt.detail.protocols)
69 }
70
71 if (JSON.stringify(evt.detail.metadata) !== JSON.stringify(metadata)) {
72 setMetadata(evt.detail.metadata)
73 }
74 }
75
76 function onPeers (evt: CustomEvent<Peer[]>): void {
77 if (JSON.stringify(evt.detail) !== JSON.stringify(peers)) {
78 setPeers(evt.detail)
79 }
80 }
81
82 events.addEventListener('metrics', onMetrics)
83 events.addEventListener('self', onSelf)
84 events.addEventListener('peers', onPeers)
85
86 return () => {
87 events.removeEventListener('metrics', onMetrics)
88 events.removeEventListener('self', onSelf)
89 events.removeEventListener('peers', onPeers)
90 }
91 }, [])
92
93 if (error != null) {
94 return (
95 <FatalErrorPanel error={error} />
96 )
97 }
98
99 if (id == null) {
100 return (
101 <ConnectingPanel />
102 )
103 }
104
105 const logo = (
106 <img src={libp2pLogo} height={24} width={24} className='Icon' />
107 )
108
109 const tabs = [{
110 name: 'Node',
111 panel: (index: string) => <Node id={id} addresses={addresses} protocols={protocols} metadata={metadata} key={`panel-${index}`} />
112 }, {
113 name: 'Peers',
114 panel: (index: string) => <Peers peers={peers} metrics={metrics} key={`panel-${index}`} />
115 }, {
116 name: 'Debug',
117 panel: (index: string) => <Debug metrics={metrics} debug={debug} key={`panel-${index}`} />
118 }, {
119 name: 'Routing',
120 panel: (index: string) => <Routing metrics={metrics} key={`panel-${index}`} />
121 }, {
122 capability: '@libp2p/ping',
123 name: 'Ping',
124 component: '',
125 panel: (index: string, component?: string) => <Ping component={component ?? ''} metrics={metrics} key={`panel-${index}`} />
126 }, {
127 capability: '@libp2p/identify',
128 name: 'Identify',
129 component: '',
130 panel: (index: string, component?: string) => <Identify component={component ?? ''} metrics={metrics} key={`panel-${index}`} />
131 }, {
132 capability: '@libp2p/pubsub',
133 name: 'PubSub',
134 component: '',
135 panel: (index: string, component?: string) => <PubSub component={component ?? ''} metrics={metrics} key={`panel-${index}`} />
136 }]
137
138 for (const tab of tabs) {
139 if (tab.capability == null) {
140 continue
141 }
142
143 const component = findComponent(capabilities, tab.capability)
144
145 if (component != null) {
146 tab.component = component
147 panels.push(tab.name)
148 }
149 }
150
151 return (
152 <>
153 <Menu logo={logo} onClick={(panel) => { setPanel(panel) }} panel={panel} options={panels} />
154 {
155 tabs.map(tab => {
156 if (panel === tab.name) {
157 return tab.panel(tab.name, tab.component)
158 }
159
160 return ''
161 })
162 }
163 </>
164 )
165 }
166
167 function findComponent (capabilities: Record<string, string[]>, capability: string): string | undefined {
168 for (const component of Object.keys(capabilities)) {
169 if (capabilities[component].includes(capability)) {
170 return component
171 }
172 }
173 }