Skip to content

Commit 97380d6

Browse files
authored
Connected cluster peers list (#19)
1 parent e592caa commit 97380d6

File tree

9 files changed

+132
-46
lines changed

9 files changed

+132
-46
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export IPNS_KEY=synthetix-node-app-config
9595
# 3. Add and pin config.json to local IPFS node
9696
export IPFS_CID=$(ipfs add --progress=false --pin=true --recursive --quieter config.json)
9797

98-
# 4. Pin config.json to Synthetix IPFS node
98+
# 4. Pin config.json to Synthetix node
9999
curl --silent --request POST --user "$IPFS_USER:$IPFS_PASS" "https://ipfs.synthetix.io:5001/api/v0/pin/add?recursive=true&progress=true&arg=$IPFS_CID" | jq
100100

101101
# 5. Publish config.json to Synthetix IPNS key

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"description": "Synthetix IPFS Node",
2+
"description": "Synthetix Node",
33
"repository": "synthetixio/snx-node",
44
"license": "MIT",
55
"author": "Synthetix Team",

src/main/follower.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { exec, spawn } from 'child_process';
22
import https from 'https';
3-
import {
4-
createReadStream,
5-
createWriteStream,
6-
promises as fs,
7-
rmSync,
8-
} from 'fs';
3+
import { createReadStream, createWriteStream, promises as fs } from 'fs';
94
import { pipeline } from 'stream/promises';
105
import os from 'os';
116
import zlib from 'zlib';
@@ -29,14 +24,6 @@ export function followerKill() {
2924
process.kill(pid);
3025
}
3126
);
32-
logger.log('Removing ~/.ipfs-cluster-follow/synthetix/badger');
33-
rmSync(path.join(IPFS_FOLLOW_PATH, 'synthetix/badger'), {
34-
recursive: true,
35-
});
36-
logger.log('Removing ~/.ipfs-cluster-follow/synthetix/api-socket');
37-
rmSync(path.join(IPFS_FOLLOW_PATH, 'synthetix/api-socket'), {
38-
recursive: true,
39-
});
4027
} catch (_e) {
4128
// whatever
4229
}

src/main/main.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ import {
2525
configureFollower,
2626
downloadFollower,
2727
follower,
28-
followerId,
2928
followerDaemon,
29+
followerId,
3030
followerIsInstalled,
3131
followerKill,
3232
followerPid,
3333
} from './follower';
34-
import { resolveDapp, DAPPS } from './dapps';
34+
import { DAPPS, resolveDapp } from './dapps';
35+
import { fetchPeers } from './peers';
3536
import { SYNTHETIX_NODE_APP_CONFIG } from '../const';
3637
import * as settings from './settings';
3738
import http from 'http';
@@ -104,7 +105,7 @@ function createWindow() {
104105
fullscreenable: false,
105106
width: 600,
106107
height: 470,
107-
frame: false,
108+
// frame: false,
108109
icon: getAssetPath('icon.icns'),
109110
webPreferences: {
110111
preload: app.isPackaged
@@ -319,6 +320,7 @@ ipcMain.handle('dapp', async (_event, id: string) => {
319320
async function resolveAllDapps() {
320321
DAPPS.forEach((dapp) => resolveDapp(dapp).then(updateContextMenu));
321322
}
323+
322324
const dappsResolver = setInterval(resolveAllDapps, 600_000); // 10 minutes
323325
app.on('will-quit', () => clearInterval(dappsResolver));
324326
waitForIpfs().then(resolveAllDapps).catch(logger.error);
@@ -342,10 +344,13 @@ async function updateConfig() {
342344
await resolveAllDapps();
343345
}
344346
}
347+
345348
const dappsUpdater = setInterval(updateConfig, 600_000); // 10 minutes
346349
app.on('will-quit', () => clearInterval(dappsUpdater));
347350
waitForIpfs().then(updateConfig).catch(logger.error);
348351

352+
ipcMain.handle('peers', async () => fetchPeers());
353+
349354
http
350355
.createServer((req, res) => {
351356
const id = `${req.headers.host}`.replace('.localhost:8888', '');

src/main/peers.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import logger from 'electron-log';
2+
import fetch from 'node-fetch';
3+
4+
Object.assign(global, { fetch });
5+
6+
export type Peer = {
7+
id: string;
8+
version: string;
9+
};
10+
11+
export async function fetchPeers(): Promise<Peer[]> {
12+
try {
13+
const response = await fetch('https://ipfs.synthetix.io/dash/api');
14+
if (response.ok) {
15+
const state = (await response.json()) as { peers: Peer[] };
16+
return state.peers.sort((a, b) => a.id.localeCompare(b.id));
17+
}
18+
return [];
19+
} catch (e) {
20+
logger.error(e);
21+
return [];
22+
}
23+
}

src/renderer/App.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
1-
import { Box, Heading, Text, Image } from '@chakra-ui/react';
1+
import { Box, Text } from '@chakra-ui/react';
22
import { Ipfs } from './Ipfs';
33
import { Dapps } from './DApps';
4-
import icon from '../../assets/icon.svg';
54

65
export default function App() {
76
return (
87
<Box display="flex" flexDirection="column" minHeight="100vh">
9-
<Box
10-
sx={{
11-
WebkitUserSelect: 'none',
12-
WebkitAppRegion: 'drag',
13-
}}
14-
background="whiteAlpha.100"
15-
px="5"
16-
py="3"
17-
display="flex"
18-
>
19-
<Image src={icon} height="26px" mr="2" />
20-
<Heading size="md">Synthetix Node</Heading>
21-
</Box>
228
<Box px="5" flex="1" overflowY="auto">
239
<Ipfs />
2410
</Box>

src/renderer/Ipfs/Ipfs.tsx

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import {
22
Box,
33
Code,
4+
Drawer,
5+
DrawerBody,
6+
DrawerCloseButton,
7+
DrawerContent,
8+
DrawerHeader,
9+
DrawerOverlay,
410
Heading,
511
Icon,
12+
IconButton,
613
Spinner,
714
Stack,
815
Stat,
916
StatLabel,
1017
StatNumber,
1118
Text,
19+
Tooltip,
1220
} from '@chakra-ui/react';
13-
import { CopyIcon } from '@chakra-ui/icons';
21+
import { ArrowRightIcon, CheckIcon, CopyIcon } from '@chakra-ui/icons';
1422
import { usePeerId } from './usePeerId';
1523
import { usePeers } from './usePeers';
1624
import { useRateIn } from './useRateIn';
@@ -21,6 +29,7 @@ import { useIsIpfsInstalled } from './useIsIpfsInstalled';
2129
import { useIsFollowerInstalled } from './useIsFollowerInstalled';
2230
import { useFollowerInfo } from './useFollowerInfo';
2331
import { SYNTHETIX_IPNS } from '../../const';
32+
import React from 'react';
2433

2534
function handleCopy(text: string) {
2635
if (text) {
@@ -65,9 +74,11 @@ export function Ipfs() {
6574
hostingSize,
6675
});
6776

77+
const [peersOpened, setPeersOpened] = React.useState(false);
78+
6879
return (
6980
<Box pt="3">
70-
<Box flex="1" p="0">
81+
<Box flex="1" p="0" whiteSpace="nowrap">
7182
<Stack direction="row" spacing={6} justifyContent="center" mb="2">
7283
<Stat>
7384
<StatLabel mb="0" opacity="0.8">
@@ -89,11 +100,74 @@ export function Ipfs() {
89100
</StatLabel>
90101
<StatNumber>{rateIn ? rateIn : '-'}</StatNumber>
91102
</Stat>
92-
<Stat>
103+
<Stat cursor="pointer" onClick={() => setPeersOpened(true)}>
93104
<StatLabel mb="0" opacity="0.8">
94-
Peers
105+
Cluster peers{' '}
106+
<IconButton
107+
aria-label="Open online peers"
108+
size="xs"
109+
icon={<ArrowRightIcon />}
110+
onClick={() => setPeersOpened(true)}
111+
/>
95112
</StatLabel>
96-
<StatNumber>{peers ? peers : '-'}</StatNumber>
113+
114+
<StatNumber>
115+
{peers ? peers.length : '-'}{' '}
116+
<Drawer
117+
isOpen={peersOpened}
118+
placement="right"
119+
onClose={() => setPeersOpened(false)}
120+
>
121+
<DrawerOverlay />
122+
<DrawerContent maxWidth="26em">
123+
<DrawerCloseButton />
124+
<DrawerHeader>Online peers</DrawerHeader>
125+
<DrawerBody>
126+
<Stack direction="column" margin="0" overflow="scroll">
127+
{peers.map((peer: { id: string }, i: number) => (
128+
<Code
129+
key={peer.id}
130+
fontSize="10px"
131+
display="block"
132+
backgroundColor="transparent"
133+
whiteSpace="nowrap"
134+
>
135+
{`${i}`.padStart(3, '0')}.{' '}
136+
<Tooltip
137+
hasArrow
138+
placement="top"
139+
openDelay={200}
140+
fontSize="xs"
141+
label={
142+
peer.id === peerId
143+
? 'Your connected Peer ID'
144+
: 'Copy Peer ID'
145+
}
146+
>
147+
<Text
148+
as="span"
149+
borderBottom="1px solid green.400"
150+
borderBottomColor={
151+
peer.id === peerId ? 'green.400' : 'transparent'
152+
}
153+
borderBottomStyle="solid"
154+
borderBottomWidth="1px"
155+
cursor="pointer"
156+
onClick={() => handleCopy(peer.id)}
157+
>
158+
{peer.id}
159+
</Text>
160+
</Tooltip>{' '}
161+
{peer.id === peerId ? (
162+
<CheckIcon color="green.400" />
163+
) : null}
164+
</Code>
165+
))}
166+
</Stack>
167+
</DrawerBody>
168+
</DrawerContent>
169+
</Drawer>
170+
</StatNumber>
97171
</Stat>
98172
</Stack>
99173
<Box bg="whiteAlpha.200" pt="4" px="4" pb="4" mb="3">

src/renderer/Ipfs/usePeers.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ export function usePeers() {
88
return useQuery({
99
queryKey: ['ipfs', 'peers'],
1010
queryFn: async () => {
11-
const peers = await ipcRenderer.invoke('ipfs-peers');
11+
const peers = await ipcRenderer.invoke('peers');
1212
if (!peers) {
13-
return 0;
13+
return [];
1414
}
15-
return peers.trim().split('\n').length;
15+
return peers;
1616
},
17-
initialData: () => 0,
18-
placeholderData: 0,
17+
initialData: () => [],
18+
placeholderData: [],
1919
enabled: Boolean(ipcRenderer && isRunning),
20+
refetchInterval: 30_000,
21+
refetchOnWindowFocus: true,
2022
});
2123
}

src/renderer/index.html

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@
33
<head>
44
<meta charset="utf-8" />
55
<meta
6-
http-equiv="Content-Security-Policy"
76
content="script-src 'self' 'unsafe-inline'"
7+
http-equiv="Content-Security-Policy"
88
/>
9-
<title>Synthetix IPFS Node</title>
9+
<title>Synthetix Node</title>
10+
<style>
11+
:focus,
12+
:focus-visible,
13+
*[data-focus] {
14+
border-color: inherit !important;
15+
outline: none !important;
16+
box-shadow: none !important;
17+
}
18+
</style>
1019
</head>
1120
<body>
1221
<div id="root"></div>

0 commit comments

Comments
 (0)