Add config score for gateway
This commit is contained in:
@@ -35,6 +35,8 @@ export default async function NymNode({
|
||||
const observatoryNymNodes: IObservatoryNode[] =
|
||||
await observatoryResponse.json();
|
||||
|
||||
console.log("observatoryNymNodes :>> ", observatoryNymNodes);
|
||||
|
||||
if (!observatoryNymNodes) {
|
||||
return null;
|
||||
}
|
||||
@@ -69,20 +71,22 @@ export default async function NymNode({
|
||||
<Grid size={12}>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<SectionHeading title="Nym Node Details" />
|
||||
<ExplorerButtonGroup
|
||||
options={[
|
||||
{
|
||||
label: "Nym Node",
|
||||
isSelected: true,
|
||||
link: `/nym-node/${id}`,
|
||||
},
|
||||
{
|
||||
label: "Account",
|
||||
isSelected: false,
|
||||
link: `/account/${observatoryNymNode.bonding_address}`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{observatoryNymNode.bonding_address && (
|
||||
<ExplorerButtonGroup
|
||||
options={[
|
||||
{
|
||||
label: "Nym Node",
|
||||
isSelected: true,
|
||||
link: `/nym-node/${id}`,
|
||||
},
|
||||
{
|
||||
label: "Account",
|
||||
isSelected: false,
|
||||
link: `/account/${observatoryNymNode.bonding_address}`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Grid>
|
||||
{observatoryNymNode && (
|
||||
@@ -95,7 +99,7 @@ export default async function NymNode({
|
||||
<NodeProfileCard nodeInfo={observatoryNymNode} />
|
||||
</Grid>
|
||||
)}
|
||||
{observatoryNymNode && (
|
||||
{observatoryNymNode.rewarding_details && (
|
||||
<Grid
|
||||
size={{
|
||||
xs: 12,
|
||||
@@ -118,17 +122,19 @@ export default async function NymNode({
|
||||
<QualityIndicatorsCard nodeInfo={observatoryNymNode} />
|
||||
</Grid>
|
||||
)}
|
||||
<Grid
|
||||
size={{
|
||||
xs: 12,
|
||||
md: 6,
|
||||
}}
|
||||
>
|
||||
<NodeRewardsCard
|
||||
rewardDetails={observatoryNymNode.rewarding_details}
|
||||
nodeInfo={observatoryNymNode}
|
||||
/>
|
||||
</Grid>
|
||||
{observatoryNymNode.rewarding_details && (
|
||||
<Grid
|
||||
size={{
|
||||
xs: 12,
|
||||
md: 6,
|
||||
}}
|
||||
>
|
||||
<NodeRewardsCard
|
||||
rewardDetails={observatoryNymNode.rewarding_details}
|
||||
nodeInfo={observatoryNymNode}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{observatoryNymNode && (
|
||||
<Grid
|
||||
size={{
|
||||
|
||||
@@ -254,3 +254,114 @@ export interface NodeRewardDetails {
|
||||
node_id: number;
|
||||
owner: string;
|
||||
}
|
||||
|
||||
export type LastProbeResult = {
|
||||
gateway: string;
|
||||
outcome: {
|
||||
as_entry: {
|
||||
can_connect: boolean;
|
||||
can_route: boolean;
|
||||
};
|
||||
as_exit: {
|
||||
can_connect: boolean;
|
||||
can_route_ip_external_v4: boolean;
|
||||
can_route_ip_external_v6: boolean;
|
||||
can_route_ip_v4: boolean;
|
||||
can_route_ip_v6: boolean;
|
||||
};
|
||||
wg: {
|
||||
can_handshake_v4: boolean;
|
||||
can_handshake_v6: boolean;
|
||||
can_register: boolean;
|
||||
can_resolve_dns_v4: boolean;
|
||||
can_resolve_dns_v6: boolean;
|
||||
ping_hosts_performance_v4: number;
|
||||
ping_hosts_performance_v6: number;
|
||||
ping_ips_performance_v4: number;
|
||||
ping_ips_performance_v6: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type GatewayStatus = {
|
||||
blacklisted: boolean;
|
||||
bonded: boolean;
|
||||
config_score: number;
|
||||
description: {
|
||||
details: string;
|
||||
moniker: string;
|
||||
security_contact: string;
|
||||
website: string;
|
||||
};
|
||||
explorer_pretty_bond: {
|
||||
identity_key: string;
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
two_letter_iso_country_code: string;
|
||||
};
|
||||
owner: string;
|
||||
pledge_amount: {
|
||||
amount: string;
|
||||
denom: string;
|
||||
};
|
||||
};
|
||||
gateway_identity_key: string;
|
||||
last_probe_log: string;
|
||||
last_probe_result: LastProbeResult; // Reference to the separate type
|
||||
last_testrun_utc: string;
|
||||
last_updated_utc: string;
|
||||
performance: number;
|
||||
routing_score: number;
|
||||
self_described: {
|
||||
authenticator: {
|
||||
address: string;
|
||||
};
|
||||
auxiliary_details: {
|
||||
accepted_operator_terms_and_conditions: boolean;
|
||||
announce_ports: {
|
||||
mix_port: number | null;
|
||||
verloc_port: number | null;
|
||||
};
|
||||
location: string;
|
||||
};
|
||||
build_information: {
|
||||
binary_name: string;
|
||||
build_timestamp: string;
|
||||
build_version: string;
|
||||
cargo_profile: string;
|
||||
cargo_triple: string;
|
||||
};
|
||||
declared_role: {
|
||||
entry: boolean;
|
||||
exit_ipr: boolean;
|
||||
exit_nr: boolean;
|
||||
mixnode: boolean;
|
||||
};
|
||||
host_information: {
|
||||
hostname: string;
|
||||
ip_address: string[];
|
||||
keys: {
|
||||
ed25519: string;
|
||||
x25519: string;
|
||||
x25519_noise: string | null;
|
||||
};
|
||||
};
|
||||
ip_packet_router: {
|
||||
address: string;
|
||||
};
|
||||
last_polled: string;
|
||||
mixnet_websockets: {
|
||||
ws_port: number;
|
||||
wss_port: number | null;
|
||||
};
|
||||
network_requester: {
|
||||
address: string;
|
||||
uses_exit_policy: boolean;
|
||||
};
|
||||
wireguard: {
|
||||
port: number;
|
||||
public_key: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import type { IObservatoryNode, NodeDescription } from "@/app/api/types";
|
||||
"use client";
|
||||
|
||||
import type {
|
||||
GatewayStatus,
|
||||
IObservatoryNode,
|
||||
LastProbeResult,
|
||||
NodeDescription,
|
||||
} from "@/app/api/types";
|
||||
import { Chip, Stack } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import ExplorerCard from "../cards/ExplorerCard";
|
||||
import ExplorerListItem from "../list/ListItem";
|
||||
import StarRating from "../starRating/StarRating";
|
||||
@@ -29,9 +37,103 @@ function getNodeRoles(
|
||||
return activeRoles;
|
||||
}
|
||||
|
||||
function calculateQualityOfServiceStars(quality: number): number {
|
||||
if (quality < 0.3) {
|
||||
return 1;
|
||||
}
|
||||
if (quality < 0.5) {
|
||||
return 2;
|
||||
}
|
||||
if (quality < 0.7) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
function calculateConfigScoreStars(probeResult: LastProbeResult): number {
|
||||
const { as_entry, as_exit } = probeResult.outcome;
|
||||
|
||||
if (as_entry && as_exit) {
|
||||
// Combine all true/false values for as_entry and as_exit
|
||||
const allResults = [
|
||||
as_entry.can_connect,
|
||||
as_entry.can_route,
|
||||
as_exit.can_connect,
|
||||
as_exit.can_route_ip_external_v4,
|
||||
as_exit.can_route_ip_external_v6,
|
||||
as_exit.can_route_ip_v4,
|
||||
as_exit.can_route_ip_v6,
|
||||
];
|
||||
|
||||
const combinedScore = allResults.filter(Boolean).length;
|
||||
|
||||
if (combinedScore === 7) {
|
||||
return 4; // 4 stars if all 7 are true
|
||||
}
|
||||
if (combinedScore === 6) {
|
||||
return 3; // 3 stars if 6 are true
|
||||
}
|
||||
if (combinedScore === 5) {
|
||||
return 2; // 2 stars if 5 are true
|
||||
}
|
||||
return 1; // 1 star if less than 5 are true
|
||||
}
|
||||
|
||||
// Check if only as_entry exists and calculate stars
|
||||
if (as_entry) {
|
||||
const { can_connect, can_route } = as_entry;
|
||||
|
||||
const entryScore = [can_connect, can_route].filter(Boolean).length;
|
||||
|
||||
if (entryScore === 2) {
|
||||
return 4; // 4 stars if both are true
|
||||
}
|
||||
if (entryScore === 1) {
|
||||
return 2; // 2 stars if one is true
|
||||
}
|
||||
return 1; // 1 star if both are false
|
||||
}
|
||||
|
||||
// Check if only as_exit exists and calculate stars
|
||||
if (as_exit) {
|
||||
const {
|
||||
can_connect,
|
||||
can_route_ip_external_v4,
|
||||
can_route_ip_external_v6,
|
||||
can_route_ip_v4,
|
||||
can_route_ip_v6,
|
||||
} = as_exit;
|
||||
|
||||
const exitScore = [
|
||||
can_connect,
|
||||
can_route_ip_external_v4,
|
||||
can_route_ip_external_v6,
|
||||
can_route_ip_v4,
|
||||
can_route_ip_v6,
|
||||
].filter(Boolean).length;
|
||||
|
||||
if (exitScore === 5) {
|
||||
return 4; // 4 stars if all 5 are true
|
||||
}
|
||||
if (exitScore === 4) {
|
||||
return 3; // 3 stars if 4 true, 1 false
|
||||
}
|
||||
if (exitScore === 3) {
|
||||
return 2; // 2 stars if 3 true, 2 false
|
||||
}
|
||||
return 1; // 1 star if 2 true or less
|
||||
}
|
||||
|
||||
// Default case if neither as_entry nor as_exit is present
|
||||
return 0; // No stars
|
||||
}
|
||||
|
||||
export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
|
||||
const { nodeInfo } = props;
|
||||
|
||||
const [gatewayProbeResult, setGatewayProbeResult] =
|
||||
useState<LastProbeResult>();
|
||||
|
||||
const nodeRoles = getNodeRoles(nodeInfo.description.declared_role);
|
||||
const NodeRoles = nodeRoles.map((role) => (
|
||||
<Stack key={role} direction="row" gap={1}>
|
||||
@@ -39,18 +141,6 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
|
||||
</Stack>
|
||||
));
|
||||
|
||||
function calculateQualityOfServiceStars(quality: number): number {
|
||||
if (quality < 0.3) {
|
||||
return 1;
|
||||
}
|
||||
if (quality < 0.5) {
|
||||
return 2;
|
||||
}
|
||||
if (quality < 0.7) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
const qualityOfServiceStars = nodeInfo?.uptime
|
||||
? calculateQualityOfServiceStars(nodeInfo?.uptime)
|
||||
: 1;
|
||||
@@ -58,6 +148,33 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
|
||||
const nodeIsMixNodeOnly =
|
||||
NodeRoles.length === 1 && nodeRoles[0] === "Mix Node";
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch data if the node has certain roles
|
||||
if (
|
||||
nodeRoles.includes("Entry Node") ||
|
||||
nodeRoles.includes("Exit IPR Node") ||
|
||||
nodeRoles.includes("Exit NR Node")
|
||||
) {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mainnet-node-status-api.nymtech.cc/v2/gateways/${nodeInfo.identity_key}`,
|
||||
);
|
||||
const data: GatewayStatus = await response.json();
|
||||
setGatewayProbeResult(data.last_probe_result);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}
|
||||
}, [nodeRoles, nodeInfo.identity_key]);
|
||||
|
||||
const configScoreStars = gatewayProbeResult
|
||||
? calculateConfigScoreStars(gatewayProbeResult)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<ExplorerCard label="Quality indicatiors" sx={{ height: "100%" }}>
|
||||
<ExplorerListItem
|
||||
@@ -70,20 +187,18 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
{nodeIsMixNodeOnly && (
|
||||
<ExplorerListItem
|
||||
row
|
||||
divider
|
||||
label="Quality of service"
|
||||
value={<StarRating value={qualityOfServiceStars} />}
|
||||
/>
|
||||
)}
|
||||
<ExplorerListItem
|
||||
row
|
||||
divider
|
||||
label="Quality of service"
|
||||
value={<StarRating value={qualityOfServiceStars} />}
|
||||
/>
|
||||
{!nodeIsMixNodeOnly && (
|
||||
<ExplorerListItem
|
||||
row
|
||||
divider
|
||||
label="Config score"
|
||||
value={<StarRating value={4} />}
|
||||
value={<StarRating value={configScoreStars} />}
|
||||
/>
|
||||
)}
|
||||
{!nodeIsMixNodeOnly && (
|
||||
|
||||
Reference in New Issue
Block a user