Skip to content

Commit 55bfbc8

Browse files
authored
Merge pull request #29 from Johnaverse/feature/rpc-monitoring-and-ui-optimization
Feature/rpc monitoring and UI optimization
2 parents 4f9e166 + 0affcca commit 55bfbc8

14 files changed

Lines changed: 412 additions & 70 deletions

dataService.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,33 @@ export function getAllChains() {
10321032
return cachedData.indexed.all.map(transformChain);
10331033
}
10341034

1035+
/**
1036+
* Count chains grouped by tag category
1037+
* @param {Array} chains - Array of chain objects
1038+
* @returns {Object} Counts for each category
1039+
*/
1040+
export function countChainsByTag(chains) {
1041+
const totalChains = chains.length;
1042+
let totalTestnets = 0;
1043+
let totalL2s = 0;
1044+
let totalBeacons = 0;
1045+
let totalMainnets = 0;
1046+
1047+
for (const chain of chains) {
1048+
const tags = chain.tags || [];
1049+
const isTestnet = tags.includes('Testnet');
1050+
const isL2 = tags.includes('L2');
1051+
const isBeacon = tags.includes('Beacon');
1052+
1053+
if (isTestnet) totalTestnets += 1;
1054+
if (isL2) totalL2s += 1;
1055+
if (isBeacon) totalBeacons += 1;
1056+
if (!isTestnet && !isL2 && !isBeacon) totalMainnets += 1;
1057+
}
1058+
1059+
return { totalChains, totalMainnets, totalTestnets, totalL2s, totalBeacons };
1060+
}
1061+
10351062
/**
10361063
* Add value to a keyword set if it is a non-empty string
10371064
*/

index.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import Fastify from 'fastify';
22
import cors from '@fastify/cors';
33
import rateLimit from '@fastify/rate-limit';
44
import helmet from '@fastify/helmet';
5+
import fastifyStatic from '@fastify/static';
56
import { readFile } from 'node:fs/promises';
6-
import { basename, resolve } from 'node:path';
7-
import { loadData, initializeDataOnStartup, getCachedData, searchChains, getChainById, getAllChains, getAllRelations, getRelationsById, getEndpointsById, getAllEndpoints, getAllKeywords, validateChainData, traverseRelations } from './dataService.js';
7+
import { basename, resolve, dirname, join } from 'node:path';
8+
import { fileURLToPath as toFilePath } from 'node:url';
9+
import { loadData, initializeDataOnStartup, getCachedData, searchChains, getChainById, getAllChains, getAllRelations, getRelationsById, getEndpointsById, getAllEndpoints, getAllKeywords, validateChainData, traverseRelations, countChainsByTag } from './dataService.js';
810
import { getMonitoringResults, getMonitoringStatus, startRpcHealthCheck } from './rpcMonitor.js';
911
import {
1012
PORT, HOST, BODY_LIMIT, MAX_PARAM_LENGTH,
@@ -51,11 +53,22 @@ export async function buildApp(options = {}) {
5153
directives: {
5254
defaultSrc: ["'self'"],
5355
scriptSrc: ["'self'"],
54-
styleSrc: ["'self'"]
56+
styleSrc: ["'self'"],
57+
fontSrc: ["'self'"],
58+
connectSrc: ["'self'"],
59+
imgSrc: ["'self'", "data:"]
5560
}
5661
}
5762
});
5863

64+
// Serve public/ directory for the 3D visualization UI
65+
const __dir = dirname(toFilePath(import.meta.url));
66+
await fastify.register(fastifyStatic, {
67+
root: join(__dir, 'public'),
68+
prefix: '/ui/',
69+
decorateReply: false
70+
});
71+
5972
// Security: Rate limiting
6073
await fastify.register(rateLimit, {
6174
max: RATE_LIMIT_MAX,
@@ -75,7 +88,7 @@ export async function buildApp(options = {}) {
7588
/**
7689
* Health check endpoint
7790
*/
78-
fastify.get('/health', async (request, reply) => {
91+
fastify.get('/health', async () => {
7992
const cachedData = getCachedData();
8093
return {
8194
status: 'ok',
@@ -157,7 +170,7 @@ export async function buildApp(options = {}) {
157170
/**
158171
* Get all chain relations
159172
*/
160-
fastify.get('/relations', async (request, reply) => {
173+
fastify.get('/relations', async () => {
161174
const relations = getAllRelations();
162175

163176
return relations;
@@ -205,7 +218,7 @@ export async function buildApp(options = {}) {
205218
/**
206219
* Get all endpoints
207220
*/
208-
fastify.get('/endpoints', async (request, reply) => {
221+
fastify.get('/endpoints', async () => {
209222
const endpoints = getAllEndpoints();
210223

211224
return {
@@ -234,7 +247,7 @@ export async function buildApp(options = {}) {
234247
/**
235248
* Get raw data sources
236249
*/
237-
fastify.get('/sources', async (request, reply) => {
250+
fastify.get('/sources', async () => {
238251
const cachedData = getCachedData();
239252
return {
240253
lastUpdated: cachedData.lastUpdated,
@@ -281,7 +294,7 @@ export async function buildApp(options = {}) {
281294
/**
282295
* Get SLIP-0044 coin types as JSON
283296
*/
284-
fastify.get('/slip44', async (request, reply) => {
297+
fastify.get('/slip44', async (_request, reply) => {
285298
const cachedData = getCachedData();
286299

287300
if (!cachedData.slip44) {
@@ -366,7 +379,7 @@ export async function buildApp(options = {}) {
366379
/**
367380
* Get RPC monitoring results
368381
*/
369-
fastify.get('/rpc-monitor', async (request, reply) => {
382+
fastify.get('/rpc-monitor', async () => {
370383
const results = getMonitoringResults();
371384
const status = getMonitoringStatus();
372385

@@ -409,15 +422,11 @@ export async function buildApp(options = {}) {
409422
/**
410423
* Get aggregate stats
411424
*/
412-
fastify.get('/stats', async (request, reply) => {
425+
fastify.get('/stats', async () => {
413426
const chains = getAllChains();
414427
const monitorResults = getMonitoringResults();
415428

416-
const totalChains = chains.length;
417-
const totalMainnets = chains.filter(c => !c.tags?.includes('Testnet') && !c.tags?.includes('L2') && !c.tags?.includes('Beacon')).length;
418-
const totalTestnets = chains.filter(c => c.tags?.includes('Testnet')).length;
419-
const totalL2s = chains.filter(c => c.tags?.includes('L2')).length;
420-
const totalBeacons = chains.filter(c => c.tags?.includes('Beacon')).length;
429+
const { totalChains, totalMainnets, totalTestnets, totalL2s, totalBeacons } = countChainsByTag(chains);
421430

422431
const rpcWorking = monitorResults.workingEndpoints;
423432
const rpcFailed = monitorResults.failedEndpoints || 0;
@@ -487,10 +496,9 @@ export async function buildApp(options = {}) {
487496
/**
488497
* Parse and validate an integer parameter
489498
* @param {string} param - Parameter value to parse
490-
* @param {string} paramName - Name of the parameter for error message
491499
* @returns {number|null} Parsed integer or null if invalid
492500
*/
493-
function parseIntParam(param, paramName = 'ID') {
501+
function parseIntParam(param) {
494502
const parsed = Number.parseInt(param, 10);
495503
return Number.isNaN(parsed) ? null : parsed;
496504
}
@@ -507,12 +515,10 @@ function sendError(reply, code, message) {
507515

508516
// Only run the server if this file is executed directly (CLI mode)
509517
// This allows the file to be imported for testing without starting the server
510-
import { fileURLToPath } from 'node:url';
511-
512-
const __filename = fileURLToPath(import.meta.url);
518+
const __filename = toFilePath(import.meta.url);
513519

514520
// Check if this file is being run directly
515-
const isMainModule = process.argv[1] === __filename || process.argv[1] === fileURLToPath(import.meta.url);
521+
const isMainModule = process.argv[1] === __filename;
516522

517523
if (isMainModule) {
518524
const start = async () => {

0 commit comments

Comments
 (0)