Skip to content

Commit e96a344

Browse files
Copilotadameat
andcommitted
feat: add network peer information to node page
Co-authored-by: adameat <[email protected]>
1 parent 9a61fde commit e96a344

File tree

6 files changed

+111
-12
lines changed

6 files changed

+111
-12
lines changed

src/components/FullNodeViewer/FullNodeViewer.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {Flex} from '@gravity-ui/uikit';
2+
import {skipToken} from '@reduxjs/toolkit/query';
23

4+
import {nodeApi} from '../../store/reducers/node/node';
35
import type {PreparedNode} from '../../store/reducers/node/types';
46
import {cn} from '../../utils/cn';
57
import {useNodeDeveloperUIHref} from '../../utils/hooks/useNodeDeveloperUIHref';
@@ -10,6 +12,7 @@ import {PoolUsage} from '../PoolUsage/PoolUsage';
1012
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
1113
import {NodeUptime} from '../UptimeViewer/UptimeViewer';
1214

15+
import {NodeNetworkInfo} from './NodeNetworkInfo';
1316
import i18n from './i18n';
1417

1518
import './FullNodeViewer.scss';
@@ -19,14 +22,20 @@ const b = cn('full-node-viewer');
1922
interface FullNodeViewerProps {
2023
node?: PreparedNode;
2124
className?: string;
25+
database?: string;
2226
}
2327
const getLoadAverageIntervalTitle = (index: number) => {
2428
return [i18n('la-interval-1m'), i18n('la-interval-5m'), i18n('la-interval-15m')][index];
2529
};
2630

27-
export const FullNodeViewer = ({node, className}: FullNodeViewerProps) => {
31+
export const FullNodeViewer = ({node, className, database}: FullNodeViewerProps) => {
2832
const developerUIHref = useNodeDeveloperUIHref(node);
2933

34+
const nodeId = node?.NodeId?.toString();
35+
const {currentData: nodeNetworkInfo} = nodeApi.useGetNodeNetworkInfoQuery(
36+
nodeId && database ? {nodeId, database} : skipToken,
37+
);
38+
3039
const commonInfo: InfoViewerItem[] = [];
3140

3241
if (node?.Tenants?.length) {
@@ -86,6 +95,14 @@ export const FullNodeViewer = ({node, className}: FullNodeViewerProps) => {
8695
info={endpointsInfo}
8796
/>
8897
) : null}
98+
99+
{nodeNetworkInfo ? (
100+
<NodeNetworkInfo
101+
nodeNetworkInfo={nodeNetworkInfo}
102+
className={b('section')}
103+
database={database}
104+
/>
105+
) : null}
89106
</Flex>
90107

91108
<Flex direction="column" gap={2}>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {getDefaultNodePath} from '../../containers/Node/NodePages';
2+
import type {TNodeInfo} from '../../types/api/nodes';
3+
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
4+
import {InfoViewer} from '../InfoViewer/InfoViewer';
5+
import type {InfoViewerItem} from '../InfoViewer/InfoViewer';
6+
import {InternalLink} from '../InternalLink';
7+
8+
import i18n from './i18n';
9+
10+
interface NodeNetworkInfoProps {
11+
nodeNetworkInfo?: TNodeInfo | null;
12+
className?: string;
13+
database?: string;
14+
}
15+
16+
export const NodeNetworkInfo = ({nodeNetworkInfo, className, database}: NodeNetworkInfoProps) => {
17+
const peers = nodeNetworkInfo?.Peers;
18+
19+
if (!peers || peers.length === 0) {
20+
return null;
21+
}
22+
23+
const connectedPeers = peers.filter((peer) => peer.Connected);
24+
const totalPeers = peers.length;
25+
26+
const networkInfo: InfoViewerItem[] = [
27+
{
28+
label: i18n('network.total-peers'),
29+
value: totalPeers,
30+
},
31+
{
32+
label: i18n('network.connected-peers'),
33+
value: connectedPeers.length,
34+
},
35+
];
36+
37+
// Show up to 5 peers in the info section
38+
const displayPeers = peers.slice(0, 5);
39+
40+
displayPeers.forEach((peer, index) => {
41+
const nodeLink = peer.NodeId ? (
42+
<InternalLink to={getDefaultNodePath(peer.NodeId, {database})}>
43+
{peer.NodeId}
44+
</InternalLink>
45+
) : (
46+
EMPTY_DATA_PLACEHOLDER
47+
);
48+
49+
networkInfo.push({
50+
label: `${i18n('network.peer-node-id')} ${index + 1}`,
51+
value: nodeLink,
52+
});
53+
});
54+
55+
return <InfoViewer title={i18n('title.network')} className={className} info={networkInfo} />;
56+
};

src/components/FullNodeViewer/i18n/en.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,15 @@
1717
"title.endpoints": "Endpoints",
1818
"title.roles": "Roles",
1919
"title.pools": "Pools",
20-
"title.load-average": "Load average"
20+
"title.load-average": "Load average",
21+
"title.network": "Network",
22+
23+
"network.connected-peers": "Connected peers",
24+
"network.total-peers": "Total peers",
25+
"network.peer-node-id": "Node ID",
26+
"network.peer-host": "Host",
27+
"network.peer-dc": "DC",
28+
"network.peer-rack": "Rack",
29+
"network.peer-connected": "Connected",
30+
"network.peer-status": "Status"
2131
}

src/containers/Node/Node.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export function Node() {
109109
{<NodePageMeta node={node} loading={pageLoading} />}
110110
{<NodePageTitle node={node} />}
111111
{error ? <ResponseError error={error} className={b('error')} /> : null}
112-
{<NodePageInfo node={node} loading={pageLoading} />}
112+
{<NodePageInfo node={node} loading={pageLoading} database={tenantName} />}
113113
{nodeId ? (
114114
<NodePageContent
115115
nodeId={nodeId}
@@ -174,14 +174,15 @@ function NodePageTitle({node}: NodePageTitleProps) {
174174
interface NodePageInfoProps {
175175
node?: PreparedNode;
176176
loading?: boolean;
177+
database?: string;
177178
}
178179

179-
function NodePageInfo({node, loading}: NodePageInfoProps) {
180+
function NodePageInfo({node, loading, database}: NodePageInfoProps) {
180181
if (loading) {
181182
return <InfoViewerSkeleton className={b('info')} rows={10} />;
182183
}
183184

184-
return <FullNodeViewer node={node} className={b('info')} />;
185+
return <FullNodeViewer node={node} className={b('info')} database={database} />;
185186
}
186187

187188
interface NodePageContentProps {

src/store/reducers/node/node.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ export const nodeApi = api.injectEndpoints({
1515
},
1616
providesTags: ['All'],
1717
}),
18+
getNodeNetworkInfo: build.query({
19+
queryFn: async ({nodeId, database}: {nodeId: string; database?: string}, {signal}) => {
20+
try {
21+
const data = await window.api.viewer.getNodes(
22+
{
23+
node_id: nodeId,
24+
database,
25+
fieldsRequired: ['Peers'],
26+
},
27+
{signal},
28+
);
29+
return {data: data.Nodes?.[0] || null};
30+
} catch (error) {
31+
return {error};
32+
}
33+
},
34+
providesTags: ['All'],
35+
}),
1836
getNodeStructure: build.query({
1937
queryFn: async ({nodeId}: {nodeId: string}, {signal}) => {
2038
try {

src/utils/yaMetrica.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import {uiFactory} from '../uiFactory/uiFactory';
22

33
/**
44
* Interface for a counter that provides methods for tracking metrics.
5-
*
6-
* @method hit - Tracks a hit event with optional arguments. https://yandex.ru/support/metrica/ru/objects/hit
7-
* @method params - Sets parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/params-method
8-
* @method userParams - Sets user-specific parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/user-params
9-
* @method reachGoal - Tracks a goal achievement event with optional arguments. https://yandex.ru/support/metrica/ru/objects/reachgoal
5+
* @function hit - Tracks a hit event with optional arguments. https://yandex.ru/support/metrica/ru/objects/hit
6+
* @function params - Sets parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/params-method
7+
* @function userParams - Sets user-specific parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/user-params
8+
* @function reachGoal - Tracks a goal achievement event with optional arguments. https://yandex.ru/support/metrica/ru/objects/reachgoal
109
*/
1110
export interface Counter {
1211
hit: (...args: unknown[]) => void;
@@ -21,7 +20,6 @@ const yaMetricaMap = uiFactory.yaMetricaMap;
2120
* A fake implementation of a counter metric for Yandex.Metrica.
2221
* This class is used when the actual Yandex.Metrica counter is not defined,
2322
* and it provides a warning message the first time any of its methods are called.
24-
*
2523
* @property name - The name of the counter.
2624
* @property warnShown - Flag to indicate if the warning has been shown.
2725
*/
@@ -61,7 +59,6 @@ class FakeMetrica implements Counter {
6159
/**
6260
* Retrieves a Yandex Metrica instance by name from the global window object.
6361
* If no instance is found for the given name, returns a FakeMetrica instance instead.
64-
*
6562
* @param name The name of the metrica to retrieve
6663
* @returns The Yandex Metrica instance if found, otherwise a FakeMetrica instance
6764
*/

0 commit comments

Comments
 (0)